Try debloating

This commit is contained in:
Venny 2020-07-16 21:49:22 +02:00
parent c6aa6fad4e
commit 89cdaf4e07
92 changed files with 3 additions and 6134 deletions

View File

@ -37,6 +37,4 @@ dependencies {
api project(':play-services-cast-api')
api project(':play-services-cast-framework-api')
api project(':play-services-iid-api')
api project(':play-services-location-api')
api project(':play-services-wearable-api')
}

View File

@ -1,3 +0,0 @@
package com.google.android.gms.maps;
parcelable GoogleMapOptions;

View File

@ -1,20 +0,0 @@
package com.google.android.gms.maps.internal;
import com.google.android.gms.dynamic.IObjectWrapper;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;
interface ICameraUpdateFactoryDelegate {
IObjectWrapper zoomIn();
IObjectWrapper zoomOut();
IObjectWrapper scrollBy(float x, float y);
IObjectWrapper zoomTo(float zoom);
IObjectWrapper zoomBy(float zoomDelta);
IObjectWrapper zoomByWithFocus(float zoomDelta, int x, int y);
IObjectWrapper newCameraPosition(in CameraPosition cameraPosition);
IObjectWrapper newLatLng(in LatLng latLng);
IObjectWrapper newLatLngZoom(in LatLng latLng, float zoom);
IObjectWrapper newLatLngBounds(in LatLngBounds bounds, int padding);
IObjectWrapper newLatLngBoundsWithSize(in LatLngBounds bounds, int width, int height, int padding);
}

View File

@ -1,6 +0,0 @@
package com.google.android.gms.maps.internal;
interface ICancelableCallback {
void onFinish();
void onCancel();
}

View File

@ -1,17 +0,0 @@
package com.google.android.gms.maps.internal;
import com.google.android.gms.dynamic.IObjectWrapper;
import com.google.android.gms.maps.GoogleMapOptions;
import com.google.android.gms.maps.internal.IMapFragmentDelegate;
import com.google.android.gms.maps.internal.IMapViewDelegate;
import com.google.android.gms.maps.internal.ICameraUpdateFactoryDelegate;
import com.google.android.gms.maps.model.internal.IBitmapDescriptorFactoryDelegate;
interface ICreator {
void init(IObjectWrapper resources);
IMapFragmentDelegate newMapFragmentDelegate(IObjectWrapper activity);
IMapViewDelegate newMapViewDelegate(IObjectWrapper context, in GoogleMapOptions options);
ICameraUpdateFactoryDelegate newCameraUpdateFactoryDelegate();
IBitmapDescriptorFactoryDelegate newBitmapDescriptorFactoryDelegate();
void initV2(IObjectWrapper resources, int flags);
}

View File

@ -1,143 +0,0 @@
package com.google.android.gms.maps.internal;
import android.location.Location;
import com.google.android.gms.dynamic.IObjectWrapper;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.internal.ICancelableCallback;
import com.google.android.gms.maps.internal.ILocationSourceDelegate;
import com.google.android.gms.maps.internal.IUiSettingsDelegate;
import com.google.android.gms.maps.internal.IProjectionDelegate;
import com.google.android.gms.maps.internal.IOnCameraChangeListener;
import com.google.android.gms.maps.internal.IOnCameraIdleListener;
import com.google.android.gms.maps.internal.IOnCameraMoveCanceledListener;
import com.google.android.gms.maps.internal.IOnCameraMoveListener;
import com.google.android.gms.maps.internal.IOnCameraMoveStartedListener;
import com.google.android.gms.maps.internal.IOnMapClickListener;
import com.google.android.gms.maps.internal.IOnMapLongClickListener;
import com.google.android.gms.maps.internal.IOnMarkerClickListener;
import com.google.android.gms.maps.internal.IOnMarkerDragListener;
import com.google.android.gms.maps.internal.IOnInfoWindowClickListener;
import com.google.android.gms.maps.internal.IInfoWindowAdapter;
import com.google.android.gms.maps.internal.IOnMapLoadedCallback;
import com.google.android.gms.maps.internal.IOnMyLocationChangeListener;
import com.google.android.gms.maps.internal.IOnMyLocationButtonClickListener;
import com.google.android.gms.maps.internal.ISnapshotReadyCallback;
import com.google.android.gms.maps.model.CircleOptions;
import com.google.android.gms.maps.model.GroundOverlayOptions;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.PolygonOptions;
import com.google.android.gms.maps.model.PolylineOptions;
import com.google.android.gms.maps.model.TileOverlayOptions;
import com.google.android.gms.maps.model.internal.IPolylineDelegate;
import com.google.android.gms.maps.model.internal.IPolygonDelegate;
import com.google.android.gms.maps.model.internal.IMarkerDelegate;
import com.google.android.gms.maps.model.internal.ICircleDelegate;
import com.google.android.gms.maps.model.internal.IGroundOverlayDelegate;
import com.google.android.gms.maps.model.internal.ITileOverlayDelegate;
interface IGoogleMapDelegate {
CameraPosition getCameraPosition() = 0;
float getMaxZoomLevel() = 1;
float getMinZoomLevel() = 2;
void moveCamera(IObjectWrapper cameraUpdate) = 3;
void animateCamera(IObjectWrapper cameraUpdate) = 4;
void animateCameraWithCallback(IObjectWrapper cameraUpdate, ICancelableCallback callback) = 5;
void animateCameraWithDurationAndCallback(IObjectWrapper cameraUpdate, int duration, ICancelableCallback callback) = 6;
void stopAnimation() = 7;
IPolylineDelegate addPolyline(in PolylineOptions options) = 8;
IPolygonDelegate addPolygon(in PolygonOptions options) = 9;
IMarkerDelegate addMarker(in MarkerOptions options) = 10;
IGroundOverlayDelegate addGroundOverlay(in GroundOverlayOptions options) = 11;
ITileOverlayDelegate addTileOverlay(in TileOverlayOptions options) = 12;
void clear() = 13;
int getMapType() = 14;
void setMapType(int type) = 15;
boolean isTrafficEnabled() = 16;
void setTrafficEnabled(boolean traffic) = 17;
boolean isIndoorEnabled() = 18;
void setIndoorEnabled(boolean indoor) = 19;
boolean isMyLocationEnabled() = 20;
void setMyLocationEnabled(boolean myLocation) = 21;
Location getMyLocation() = 22;
void setLocationSource(ILocationSourceDelegate locationSource) = 23;
IUiSettingsDelegate getUiSettings() = 24;
IProjectionDelegate getProjection() = 25;
void setOnCameraChangeListener(IOnCameraChangeListener listener) = 26;
void setOnMapClickListener(IOnMapClickListener listener) = 27;
void setOnMapLongClickListener(IOnMapLongClickListener listener) = 28;
void setOnMarkerClickListener(IOnMarkerClickListener listener) = 29;
void setOnMarkerDragListener(IOnMarkerDragListener listener) = 30;
void setOnInfoWindowClickListener(IOnInfoWindowClickListener listener) = 31;
void setInfoWindowAdapter(IInfoWindowAdapter adapter) = 32;
IObjectWrapper getTestingHelper() = 33;
ICircleDelegate addCircle(in CircleOptions options) = 34;
void setOnMyLocationChangeListener(IOnMyLocationChangeListener listener) = 35;
void setOnMyLocationButtonClickListener(IOnMyLocationButtonClickListener listener) = 36;
void snapshot(ISnapshotReadyCallback callback, IObjectWrapper bitmap) = 37;
void setPadding(int left, int top, int right, int bottom) = 38;
boolean isBuildingsEnabled() = 39;
void setBuildingsEnabled(boolean buildings) = 40;
void setOnMapLoadedCallback(IOnMapLoadedCallback callback) = 41;
//IIndoorBuildingDelegate getFocusedBuilding() = 43;
//void setIndoorStateChangeListener(IOnIndoorStateChangeListener listener) = 44;
void setWatermarkEnabled(boolean watermark) = 50;
//void getMapAsync(IOnMapReadyCallback callback) = 52;
void onCreate(in Bundle savedInstanceState) = 53;
void onResume() = 54;
void onPause() = 55;
void onDestroy() = 56;
void onLowMemory() = 57;
boolean useViewLifecycleWhenInFragment() = 58;
void onSaveInstanceState(out Bundle outState) = 59;
void setContentDescription(String desc) = 60;
//void snapshotForTest(ISnapshotReadyCallback callback) = 70;
//void setPoiClickListener(IOnPoiClickListener listener) = 79;
void onEnterAmbient(in Bundle bundle) = 80;
void onExitAmbient() = 81;
//void setOnGroundOverlayClickListener(IOnGroundOverlayClickListener listener) = 82;
//void setInfoWindowLongClickListener(IOnInfoWindowLongClickListener listener) = 83;
//void setPolygonClickListener(IOnPolygonClickListener listener) = 84;
//void setInfoWindowCloseListener(IOnInfoWindowCloseListener listener) = 85;
//void setPolylineClickListener(IOnPolylineClickListener listener) = 86;
//void setCircleClickListener(IOnCircleClickListener listener) = 88;
//boolean setMapStyle(in MapStyleOptions options) = 90;
void setMinZoomPreference(float minZoom) = 91;
void setMaxZoomPreference(float maxZoom) = 92;
void resetMinMaxZoomPreference() = 93;
void setLatLngBoundsForCameraTarget(in LatLngBounds bounds) = 94;
void setCameraMoveStartedListener(IOnCameraMoveStartedListener listener) = 95;
void setCameraMoveListener(IOnCameraMoveListener listener) = 96;
void setCameraMoveCanceledListener(IOnCameraMoveCanceledListener listener) = 97;
void setCameraIdleListener(IOnCameraIdleListener listener) = 98;
void onStart() = 100;
void onStop() = 101;
//void setOnMyLocationClickListener(IOnMyLocationClickListener listener) = 106;
}

View File

@ -1,9 +0,0 @@
package com.google.android.gms.maps.internal;
import com.google.android.gms.dynamic.IObjectWrapper;
import com.google.android.gms.maps.model.internal.IMarkerDelegate;
interface IInfoWindowAdapter {
IObjectWrapper getInfoWindow(IMarkerDelegate marker);
IObjectWrapper getInfoContents(IMarkerDelegate marker);
}

View File

@ -1,4 +0,0 @@
package com.google.android.gms.maps.internal;
interface ILocationSourceDelegate {
}

View File

@ -1,27 +0,0 @@
package com.google.android.gms.maps.internal;
import android.os.Bundle;
import com.google.android.gms.maps.internal.IGoogleMapDelegate;
import com.google.android.gms.maps.internal.IOnMapReadyCallback;
import com.google.android.gms.maps.GoogleMapOptions;
import com.google.android.gms.dynamic.IObjectWrapper;
interface IMapFragmentDelegate {
IGoogleMapDelegate getMap();
void onInflate(IObjectWrapper activity, in GoogleMapOptions options, in Bundle savedInstanceState);
void onCreate(in Bundle savedInstanceState);
IObjectWrapper onCreateView(IObjectWrapper layoutInflater, IObjectWrapper container, in Bundle savedInstanceState);
void onResume();
void onPause();
void onDestroyView();
void onDestroy();
void onLowMemory();
void onSaveInstanceState(inout Bundle outState);
boolean isReady();
void getMapAsync(IOnMapReadyCallback callback);
void onEnterAmbient(in Bundle bundle);
void onExitAmbient();
void onStart();
void onStop();
}

View File

@ -1,23 +0,0 @@
package com.google.android.gms.maps.internal;
import android.os.Bundle;
import com.google.android.gms.maps.internal.IGoogleMapDelegate;
import com.google.android.gms.maps.internal.IOnMapReadyCallback;
import com.google.android.gms.dynamic.IObjectWrapper;
interface IMapViewDelegate {
IGoogleMapDelegate getMap();
void onCreate(in Bundle savedInstanceState);
void onResume();
void onPause();
void onDestroy();
void onLowMemory();
void onSaveInstanceState(inout Bundle outState);
IObjectWrapper getView();
void getMapAsync(IOnMapReadyCallback callback);
void onEnterAmbient(in Bundle bundle);
void onExitAmbient();
void onStart();
void onStop();
}

View File

@ -1,7 +0,0 @@
package com.google.android.gms.maps.internal;
import com.google.android.gms.maps.model.CameraPosition;
interface IOnCameraChangeListener {
void onCameraChange(in CameraPosition update);
}

View File

@ -1,5 +0,0 @@
package com.google.android.gms.maps.internal;
interface IOnCameraIdleListener {
void onCameraIdle();
}

View File

@ -1,5 +0,0 @@
package com.google.android.gms.maps.internal;
interface IOnCameraMoveCanceledListener {
void onCameraMoveCanceled();
}

View File

@ -1,5 +0,0 @@
package com.google.android.gms.maps.internal;
interface IOnCameraMoveListener {
void onCameraMove();
}

View File

@ -1,5 +0,0 @@
package com.google.android.gms.maps.internal;
interface IOnCameraMoveStartedListener {
void onCameraMoveStarted(int i);
}

View File

@ -1,7 +0,0 @@
package com.google.android.gms.maps.internal;
import com.google.android.gms.maps.model.internal.IMarkerDelegate;
interface IOnInfoWindowClickListener {
void onInfoWindowClick(IMarkerDelegate marker);
}

View File

@ -1,7 +0,0 @@
package com.google.android.gms.maps.internal;
import com.google.android.gms.maps.model.LatLng;
interface IOnMapClickListener {
void onMapClick(in LatLng latLng);
}

View File

@ -1,5 +0,0 @@
package com.google.android.gms.maps.internal;
interface IOnMapLoadedCallback {
void onMapLoaded();
}

View File

@ -1,7 +0,0 @@
package com.google.android.gms.maps.internal;
import com.google.android.gms.maps.model.LatLng;
interface IOnMapLongClickListener {
void onMapLongClick(in LatLng latLng);
}

View File

@ -1,7 +0,0 @@
package com.google.android.gms.maps.internal;
import com.google.android.gms.maps.internal.IGoogleMapDelegate;
interface IOnMapReadyCallback {
void onMapReady(IGoogleMapDelegate map);
}

View File

@ -1,7 +0,0 @@
package com.google.android.gms.maps.internal;
import com.google.android.gms.maps.model.internal.IMarkerDelegate;
interface IOnMarkerClickListener {
boolean onMarkerClick(IMarkerDelegate marker);
}

View File

@ -1,9 +0,0 @@
package com.google.android.gms.maps.internal;
import com.google.android.gms.maps.model.internal.IMarkerDelegate;
interface IOnMarkerDragListener {
void onMarkerDragStart(IMarkerDelegate marker) = 0;
void onMarkerDrag(IMarkerDelegate marker) = 1;
void onMarkerDragEnd(IMarkerDelegate marker) = 2;
}

View File

@ -1,4 +0,0 @@
package com.google.android.gms.maps.internal;
interface IOnMyLocationButtonClickListener {
}

View File

@ -1,7 +0,0 @@
package com.google.android.gms.maps.internal;
import com.google.android.gms.dynamic.IObjectWrapper;
interface IOnMyLocationChangeListener {
void onMyLocationChanged(IObjectWrapper location);
}

View File

@ -1,11 +0,0 @@
package com.google.android.gms.maps.internal;
import com.google.android.gms.dynamic.IObjectWrapper;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.VisibleRegion;
interface IProjectionDelegate {
LatLng fromScreenLocation(IObjectWrapper obj);
IObjectWrapper toScreenLocation(in LatLng latLng);
VisibleRegion getVisibleRegion();
}

View File

@ -1,9 +0,0 @@
package com.google.android.gms.maps.internal;
import com.google.android.gms.dynamic.IObjectWrapper;
import android.graphics.Bitmap;
interface ISnapshotReadyCallback {
void onBitmapReady(in Bitmap bitmap);
void onBitmapWrappedReady(IObjectWrapper wrapper);
}

View File

@ -1,25 +0,0 @@
package com.google.android.gms.maps.internal;
interface IUiSettingsDelegate {
void setZoomControlsEnabled(boolean zoom);
void setCompassEnabled(boolean compass);
void setMyLocationButtonEnabled(boolean locationButton);
void setScrollGesturesEnabled(boolean scrollGestures);
void setZoomGesturesEnabled(boolean zoomGestures);
void setTiltGesturesEnabled(boolean tiltGestures);
void setRotateGesturesEnabled(boolean rotateGestures);
void setAllGesturesEnabled(boolean gestures);
boolean isZoomControlsEnabled();
boolean isCompassEnabled();
boolean isMyLocationButtonEnabled();
boolean isScrollGesturesEnabled();
boolean isZoomGesturesEnabled();
boolean isTiltGesturesEnabled();
boolean isRotateGesturesEnabled();
void setIndoorLevelPickerEnabled(boolean indoorLevelPicker);
boolean isIndoorLevelPickerEnabled();
void setMapToolbarEnabled(boolean mapToolbar);
boolean isMapToolbarEnabled();
void setScrollGesturesEnabledDuringRotateOrZoom(boolean scrollDuringZoom);
boolean isScrollGesturesEnabledDuringRotateOrZoom();
}

View File

@ -1,3 +0,0 @@
package com.google.android.gms.maps.model;
parcelable CameraPosition;

View File

@ -1,3 +0,0 @@
package com.google.android.gms.maps.model;
parcelable CircleOptions;

View File

@ -1,3 +0,0 @@
package com.google.android.gms.maps.model;
parcelable GroundOverlayOptions;

View File

@ -1,3 +0,0 @@
package com.google.android.gms.maps.model;
parcelable MarkerOptions;

View File

@ -1,3 +0,0 @@
package com.google.android.gms.maps.model;
parcelable PolygonOptions;

View File

@ -1,3 +0,0 @@
package com.google.android.gms.maps.model;
parcelable PolylineOptions;

View File

@ -1,3 +0,0 @@
package com.google.android.gms.maps.model;
parcelable Tile;

View File

@ -1,3 +0,0 @@
package com.google.android.gms.maps.model;
parcelable TileOverlayOptions;

View File

@ -1,3 +0,0 @@
package com.google.android.gms.maps.model;
parcelable VisibleRegion;

View File

@ -1,14 +0,0 @@
package com.google.android.gms.maps.model.internal;
import android.graphics.Bitmap;
import com.google.android.gms.dynamic.IObjectWrapper;
interface IBitmapDescriptorFactoryDelegate {
IObjectWrapper fromResource(int resourceId);
IObjectWrapper fromAsset(String assetName);
IObjectWrapper fromFile(String fileName);
IObjectWrapper defaultMarker();
IObjectWrapper defaultMarkerWithHue(float hue);
IObjectWrapper fromBitmap(in Bitmap bitmap);
IObjectWrapper fromPath(String absolutePath);
}

View File

@ -1,24 +0,0 @@
package com.google.android.gms.maps.model.internal;
import com.google.android.gms.maps.model.LatLng;
interface ICircleDelegate {
void remove();
String getId();
void setCenter(in LatLng center);
LatLng getCenter();
void setRadius(double radius);
double getRadius();
void setStrokeWidth(float width);
float getStrokeWidth();
void setStrokeColor(int color);
int getStrokeColor();
void setFillColor(int color);
int getFillColor();
void setZIndex(float zIndex);
float getZIndex();
void setVisible(boolean visible);
boolean isVisible();
boolean equalsRemote(ICircleDelegate other);
int hashCodeRemote();
}

View File

@ -1,29 +0,0 @@
package com.google.android.gms.maps.model.internal;
import com.google.android.gms.dynamic.IObjectWrapper;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;
interface IGroundOverlayDelegate {
void remove();
String getId();
void setPosition(in LatLng pos);
LatLng getPosition();
void setDimension(float dimension);
void setDimensions(float width, float height);
float getWidth();
float getHeight();
void setPositionFromBounds(in LatLngBounds bounds);
LatLngBounds getBounds();
void setBearing(float bearing);
float getBearing();
void setZIndex(float zIndex);
float getZIndex();
void setVisible(boolean visible);
boolean isVisible();
void setTransparency(float transparency);
float getTransparency();
boolean equalsRemote(IGroundOverlayDelegate other);
int hashCodeRemote();
void todo(IObjectWrapper obj);
}

View File

@ -1,37 +0,0 @@
package com.google.android.gms.maps.model.internal;
import com.google.android.gms.dynamic.IObjectWrapper;
import com.google.android.gms.maps.model.LatLng;
interface IMarkerDelegate {
void remove();
String getId();
void setPosition(in LatLng pos);
LatLng getPosition();
void setTitle(String title);
String getTitle();
void setSnippet(String snippet);
String getSnippet();
void setDraggable(boolean drag);
boolean isDraggable();
void showInfoWindow();
void hideInfoWindow();
boolean isInfoWindowShown();
void setVisible(boolean visible);
boolean isVisible();
boolean equalsRemote(IMarkerDelegate other);
int hashCodeRemote();
void setIcon(IObjectWrapper obj);
void setAnchor(float x, float y);
void setFlat(boolean flat);
boolean isFlat();
void setRotation(float rotation);
float getRotation();
void setInfoWindowAnchor(float x, float y);
void setAlpha(float alpha);
float getAlpha();
void setZIndex(float zIndex);
float getZIndex();
void setTag(IObjectWrapper obj);
IObjectWrapper getTag();
}

View File

@ -1,27 +0,0 @@
package com.google.android.gms.maps.model.internal;
import com.google.android.gms.maps.model.LatLng;
interface IPolygonDelegate {
void remove();
String getId();
void setPoints(in List<LatLng> points);
List<LatLng> getPoints();
void setHoles(in List holes);
List getHoles();
void setStrokeWidth(float width);
float getStrokeWidth();
void setStrokeColor(int color);
int getStrokeColor();
void setFillColor(int color);
int getFillColor();
void setZIndex(float zIndex);
float getZIndex();
void setVisible(boolean visible);
boolean isVisible();
void setGeodesic(boolean geod);
boolean isGeodesic();
boolean equalsRemote(IPolygonDelegate other);
int hashCodeRemote();
}

View File

@ -1,22 +0,0 @@
package com.google.android.gms.maps.model.internal;
import com.google.android.gms.maps.model.LatLng;
interface IPolylineDelegate {
void remove();
String getId();
void setPoints(in List<LatLng> points);
List<LatLng> getPoints();
void setWidth(float width);
float getWidth();
void setColor(int color);
int getColor();
void setZIndex(float zIndex);
float getZIndex();
void setVisible(boolean visible);
boolean isVisible();
void setGeodesic(boolean geod);
boolean isGeodesic();
boolean equalsRemote(IPolylineDelegate other);
int hashCodeRemote();
}

View File

@ -1,4 +0,0 @@
package com.google.android.gms.maps.model.internal;
interface ITileOverlayDelegate {
}

View File

@ -1,7 +0,0 @@
package com.google.android.gms.maps.model.internal;
import com.google.android.gms.maps.model.Tile;
interface ITileProviderDelegate {
Tile getTile(int x, int y, int zoom);
}

View File

@ -1,166 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.maps;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLngBounds;
import org.microg.safeparcel.AutoSafeParcelable;
import org.microg.safeparcel.SafeParceled;
public final class GoogleMapOptions extends AutoSafeParcelable {
@SafeParceled(1)
private int versionCode;
@SafeParceled(2)
private int zOrderOnTop;
@SafeParceled(3)
private boolean useViewLifecycleInFragment;
@SafeParceled(4)
private int mapType;
@SafeParceled(5)
private CameraPosition camera;
@SafeParceled(6)
private boolean zoomControlsEnabled;
@SafeParceled(7)
private boolean compassEnabled;
@SafeParceled(8)
private boolean scrollGesturesEnabled = true;
@SafeParceled(9)
private boolean zoomGesturesEnabled = true;
@SafeParceled(10)
private boolean tiltGesturesEnabled = true;
@SafeParceled(11)
private boolean rotateGesturesEnabled = true;
@SafeParceled(12)
private boolean liteMode = false;
@SafeParceled(14)
private boolean mapToobarEnabled = false;
@SafeParceled(15)
private boolean ambientEnabled = false;
@SafeParceled(16)
private float minZoom;
@SafeParceled(17)
private float maxZoom;
@SafeParceled(18)
private LatLngBounds boundsForCamera;
@SafeParceled(19)
private boolean scrollGesturesEnabledDuringRotateOrZoom = true;
public GoogleMapOptions() {
}
public Boolean getAmbientEnabled() {
return ambientEnabled;
}
public CameraPosition getCamera() {
return camera;
}
public Boolean getCompassEnabled() {
return compassEnabled;
}
public LatLngBounds getLatLngBoundsForCameraTarget() {
return boundsForCamera;
}
public Boolean getLiteMode() {
return liteMode;
}
public Boolean getMapToolbarEnabled() {
return mapToobarEnabled;
}
public int getMapType() {
return mapType;
}
public Float getMaxZoomPreference() {
return maxZoom;
}
public Float getMinZoomPreference() {
return minZoom;
}
public Boolean getRotateGesturesEnabled() {
return rotateGesturesEnabled;
}
public Boolean getScrollGesturesEnabled() {
return scrollGesturesEnabled;
}
public Boolean getScrollGesturesEnabledDuringRotateOrZoom() {
return scrollGesturesEnabledDuringRotateOrZoom;
}
public Boolean getTiltGesturesEnabled() {
return tiltGesturesEnabled;
}
public Boolean getUseViewLifecycleInFragment() {
return useViewLifecycleInFragment;
}
public Boolean getZOrderOnTop() {
return zOrderOnTop == 1; // TODO
}
public Boolean getZoomControlsEnabled() {
return zoomControlsEnabled;
}
public Boolean getZoomGesturesEnabled() {
return zoomGesturesEnabled;
}
@Deprecated
public boolean isCompassEnabled() {
return compassEnabled;
}
@Deprecated
public boolean isZoomControlsEnabled() {
return zoomControlsEnabled;
}
@Deprecated
public boolean isScrollGesturesEnabled() {
return scrollGesturesEnabled;
}
@Deprecated
public boolean isZoomGesturesEnabled() {
return zoomGesturesEnabled;
}
@Deprecated
public boolean isTiltGesturesEnabled() {
return tiltGesturesEnabled;
}
@Deprecated
public boolean isRotateGesturesEnabled() {
return rotateGesturesEnabled;
}
public static Creator<GoogleMapOptions> CREATOR = new AutoCreator<GoogleMapOptions>(GoogleMapOptions.class);
}

View File

@ -1,20 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.maps;
public class StreetViewPanoramaOptions {
}

View File

@ -1,20 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.maps.internal;
public class Point {
}

View File

@ -1,38 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.maps.model;
import com.google.android.gms.dynamic.IObjectWrapper;
import org.microg.gms.common.PublicApi;
/**
* Defines an image. For a marker, it can be used to set the image of the marker icon. For a ground
* overlay, it can be used to set the image to place on the surface of the earth.
*/
@PublicApi
public class BitmapDescriptor {
private final IObjectWrapper remoteObject;
public BitmapDescriptor(IObjectWrapper remoteObject) {
this.remoteObject = remoteObject;
}
public IObjectWrapper getRemoteObject() {
return remoteObject;
}
}

View File

@ -1,249 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.maps.model;
import android.content.Context;
import android.util.AttributeSet;
import org.microg.gms.common.PublicApi;
import org.microg.safeparcel.AutoSafeParcelable;
import org.microg.safeparcel.SafeParceled;
import java.util.Arrays;
/**
* An immutable class that aggregates all camera position parameters.
*/
@PublicApi
public final class CameraPosition extends AutoSafeParcelable {
@SafeParceled(1)
private int versionCode = 1;
/**
* The location that the camera is pointing at.
*/
@SafeParceled(2)
public final LatLng target;
/**
* Zoom level near the center of the screen.
* See {@link Builder#zoom(float)} for the definition of the camera's zoom level.
*/
@SafeParceled(3)
public final float zoom;
/**
* The angle, in degrees, of the camera angle from the nadir (directly facing the Earth).
* See {@link Builder#tilt(float)} for details of restrictions on the range of values.
*/
@SafeParceled(4)
public final float tilt;
/**
* Direction that the camera is pointing in, in degrees clockwise from north.
*/
@SafeParceled(5)
public final float bearing;
/**
* This constructor is dirty setting the final fields to make the compiler happy.
* In fact, those are replaced by their real values later using SafeParcelUtil.
*/
private CameraPosition() {
target = null;
zoom = tilt = bearing = 0;
}
/**
* Constructs a CameraPosition.
*
* @param target The target location to align with the center of the screen.
* @param zoom Zoom level at target. See {@link #zoom} for details of restrictions.
* @param tilt The camera angle, in degrees, from the nadir (directly down). See
* {@link #tilt} for details of restrictions.
* @param bearing Direction that the camera is pointing in, in degrees clockwise from north.
* This value will be normalized to be within 0 degrees inclusive and 360
* degrees exclusive.
* @throws NullPointerException if {@code target} is {@code null}
* @throws IllegalArgumentException if {@code tilt} is outside range of {@code 0} to {@code 90}
* degrees inclusive
*/
public CameraPosition(LatLng target, float zoom, float tilt, float bearing)
throws NullPointerException, IllegalArgumentException {
if (target == null) {
throw new NullPointerException("null camera target");
}
this.target = target;
this.zoom = zoom;
if (tilt < 0 || 90 < tilt) {
throw new IllegalArgumentException("Tilt needs to be between 0 and 90 inclusive");
}
this.tilt = tilt;
if (bearing <= 0) {
bearing += 360;
}
this.bearing = bearing % 360;
}
/**
* Creates a builder for a camera position.
*/
public static Builder builder() {
return new Builder();
}
/**
* Creates a builder for a camera position, initialized to a given position.
*/
public static Builder builder(CameraPosition camera) {
return new Builder(camera);
}
/**
* Creates a CameraPostion from the attribute set
*
* @throws UnsupportedOperationException
*/
public static CameraPosition createFromAttributes(Context context, AttributeSet attrs) {
return null; // TODO
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
CameraPosition that = (CameraPosition) o;
if (Float.compare(that.bearing, bearing) != 0)
return false;
if (Float.compare(that.tilt, tilt) != 0)
return false;
if (Float.compare(that.zoom, zoom) != 0)
return false;
if (!target.equals(that.target))
return false;
return true;
}
/**
* Constructs a CameraPosition pointed for a particular target and zoom level. The resultant
* bearing is North, and the viewing angle is perpendicular to the Earth's surface. i.e.,
* directly facing the Earth's surface, with the top of the screen pointing North.
*
* @param target The target location to align with the center of the screen.
* @param zoom Zoom level at target. See {@link Builder#zoom(float)} for details on the range
* the value will be clamped to. The larger the value the more zoomed in the
* camera is.
*/
public static final CameraPosition fromLatLngZoom(LatLng target, float zoom) {
return builder().target(target).zoom(zoom).build();
}
@Override
public int hashCode() {
return Arrays.hashCode(new Object[] { target, zoom, tilt, bearing });
}
@Override
public String toString() {
return "CameraPosition{" +
"target=" + target +
", zoom=" + zoom +
", tilt=" + tilt +
", bearing=" + bearing +
'}';
}
public static Creator<CameraPosition> CREATOR = new AutoCreator<CameraPosition>(CameraPosition.class);
/**
* Builds camera position.
*/
public static final class Builder {
private LatLng target;
private float zoom;
private float tilt;
private float bearing;
public Builder() {
}
public Builder(CameraPosition previous) {
target = previous.target;
zoom = previous.zoom;
tilt = previous.tilt;
bearing = previous.bearing;
}
/**
* Sets the direction that the camera is pointing in, in degrees clockwise from north.
*/
public Builder bearing(float bearing) {
this.bearing = bearing;
return this;
}
/**
* Builds a {@link CameraPosition}.
*/
public CameraPosition build() {
return new CameraPosition(target, zoom, tilt, bearing);
}
/**
* Sets the location that the camera is pointing at.
*/
public Builder target(LatLng target) {
this.target = target;
return this;
}
/**
* Sets the angle, in degrees, of the camera from the nadir (directly facing the Earth).
* When changing the camera position for a map, this value is restricted depending on the
* zoom level of the camera. The restrictions are as follows:
* <ul>
* <li>For zoom levels less than 10 the maximum is 30.</li>
* <li>For zoom levels from 10 to 14 the maximum increases linearly from 30 to 45 (e.g. at
* zoom level 12, the maximum is 37.5).</li>
* <li>For zoom levels from 14 to 15.5 the maximum increases linearly from 45 to 67.5.</li>
* <li>For zoom levels greater than 15.5 the maximum is 67.5.</li>
* </ul>
* The minimum is always 0 (directly down). If you specify a value outside this range and try to move the camera to this camera position it will be clamped to these bounds.
*/
public Builder tilt(float tilt) {
this.tilt = tilt;
return this;
}
/**
* Sets the zoom level of the camera. Zoom level is defined such that at zoom level 0, the
* whole world is approximately 256dp wide (assuming that the camera is not tilted).
* Increasing the zoom level by 1 doubles the width of the world on the screen. Hence at
* zoom level N, the width of the world is approximately 256 * 2 N dp, i.e., at zoom level
* 2, the whole world is approximately 1024dp wide.
* <p/>
* When changing the camera position for a map, the zoom level of the camera is restricted
* to a certain range depending on various factors including location, map type and map
* size. Note that the camera zoom need not be an integer value.
*/
public Builder zoom(float zoom) {
this.zoom = zoom;
return this;
}
}
}

View File

@ -1,221 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.maps.model;
import android.graphics.Color;
import org.microg.gms.common.PublicApi;
import org.microg.safeparcel.AutoSafeParcelable;
import org.microg.safeparcel.SafeParceled;
/**
* Defines options for a Circle.
*/
@PublicApi
public class CircleOptions extends AutoSafeParcelable {
@SafeParceled(1)
private int versionCode;
@SafeParceled(2)
private LatLng center;
@SafeParceled(3)
private double radius = 0;
@SafeParceled(4)
private float strokeWidth = 10;
@SafeParceled(5)
private int strokeColor = Color.BLACK;
@SafeParceled(6)
private int fillColor = Color.TRANSPARENT;
@SafeParceled(7)
private float zIndex = 0;
@SafeParceled(8)
private boolean visible = true;
/**
* Creates circle options.
*/
public CircleOptions() {
}
/**
* Sets the center using a {@link LatLng}.
* <p/>
* The center must not be {@code null}.
* <p/>
* This method is mandatory because there is no default center.
*
* @param center The geographic center as a {@link LatLng}.
* @return this {@link CircleOptions} object
*/
public CircleOptions center(LatLng center) {
this.center = center;
return this;
}
/**
* Sets the fill color.
* <p/>
* The fill color is the color inside the circle, in the integer format specified by
* {@link Color}. If TRANSPARENT is used then no fill is drawn.
* <p/>
* By default the fill color is transparent ({@code 0x00000000}).
*
* @param color color in the {@link Color} format
* @return this {@link CircleOptions} object
*/
public CircleOptions fillColor(int color) {
this.fillColor = color;
return this;
}
/**
* Returns the center as a {@link LatLng}.
*
* @return The geographic center as a {@link LatLng}.
*/
public LatLng getCenter() {
return center;
}
/**
* Returns the fill color.
*
* @return The color in the {@link Color} format.
*/
public int getFillColor() {
return fillColor;
}
/**
* Returns the circle's radius, in meters.
*
* @return The radius in meters.
*/
public double getRadius() {
return radius;
}
/**
* Returns the stroke color.
*
* @return The color in the {@link Color} format.
*/
public int getStrokeColor() {
return strokeColor;
}
/**
* Returns the stroke width.
*
* @return The width in screen pixels.
*/
public float getStrokeWidth() {
return strokeWidth;
}
/**
* Returns the zIndex.
*
* @return The zIndex value.
*/
public float getZIndex() {
return zIndex;
}
/**
* Checks whether the circle is visible.
*
* @return {code true} if the circle is visible; {@code false} if it is invisible.
*/
public boolean isVisible() {
return visible;
}
/**
* Sets the radius in meters.
* <p/>
* The radius must be zero or greater. The default radius is zero.
*
* @param radius radius in meters
* @return this {@link CircleOptions} object
*/
public CircleOptions radius(double radius) {
this.radius = radius;
return this;
}
/**
* Sets the stroke color.
* <p/>
* The stroke color is the color of this circle's outline, in the integer format specified by
* {@link Color}. If TRANSPARENT is used then no outline is drawn.
* <p/>
* By default the stroke color is black ({@code 0xff000000}).
*
* @param color color in the {@link Color} format
* @return this {@link CircleOptions} object
*/
public CircleOptions strokeColor(int color) {
this.strokeColor = color;
return this;
}
/**
* Sets the stroke width.
* <p/>
* The stroke width is the width (in screen pixels) of the circle's outline. It must be zero or
* greater. If it is zero then no outline is drawn.
* <p/>
* The default width is 10 pixels.
*
* @param width width in screen pixels
* @return this {@link CircleOptions} object
*/
public CircleOptions strokeWidth(float width) {
this.strokeWidth = width;
return this;
}
/**
* Sets the visibility.
* <p/>
* If this circle is not visible then it is not drawn, but all other state is preserved.
*
* @param visible {@code false} to make this circle invisible
* @return this {@link CircleOptions} object
*/
public CircleOptions visible(boolean visible) {
this.visible = visible;
return this;
}
/**
* Sets the zIndex.
* <p/>
* Overlays (such as circles) with higher zIndices are drawn above those with lower indices.
* <p/>
* By default the zIndex is {@code 0.0}.
*
* @param zIndex zIndex value
* @return this {@link CircleOptions} object
*/
public CircleOptions zIndex(float zIndex) {
this.zIndex = zIndex;
return this;
}
public static Creator<CircleOptions> CREATOR = new AutoCreator<CircleOptions>(CircleOptions.class);
}

View File

@ -1,341 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.maps.model;
import android.os.IBinder;
import com.google.android.gms.dynamic.ObjectWrapper;
import org.microg.gms.common.PublicApi;
import org.microg.safeparcel.AutoSafeParcelable;
import org.microg.safeparcel.SafeParceled;
/**
* Defines options for a ground overlay.
*/
@PublicApi
public class GroundOverlayOptions extends AutoSafeParcelable {
/**
* Flag for when no dimension is specified for the height.
*/
public static final float NO_DIMENSION = -1;
@SafeParceled(1)
private int versionCode;
@SafeParceled(2)
private IBinder image;
private BitmapDescriptor imageDescriptor;
@SafeParceled(3)
private LatLng location;
@SafeParceled(4)
private float width;
@SafeParceled(5)
private float height;
@SafeParceled(6)
private LatLngBounds bounds;
@SafeParceled(7)
private float bearing;
@SafeParceled(8)
private float zIndex;
@SafeParceled(9)
private boolean visible;
@SafeParceled(10)
private float transparency = 0;
@SafeParceled(11)
private float anchorU;
@SafeParceled(12)
private float anchorV;
/**
* Creates a new set of ground overlay options.
*/
public GroundOverlayOptions() {
}
/**
* Specifies the anchor to be at a particular point in the image.
* <p/>
* The anchor specifies the point in the image that aligns with the ground overlay's location.
* <p/>
* The anchor point is specified in the continuous space [0.0, 1.0] x [0.0, 1.0], where (0, 0)
* is the top-left corner of the image, and (1, 1) is the bottom-right corner.
*
* @param u u-coordinate of the anchor, as a ratio of the image width (in the range [0, 1])
* @param v v-coordinate of the anchor, as a ratio of the image height (in the range [0, 1])
* @return this {@link GroundOverlayOptions} object with a new anchor set.
*/
public GroundOverlayOptions anchor(float u, float v) {
this.anchorU = u;
this.anchorV = v;
return this;
}
/**
* Specifies the bearing of the ground overlay in degrees clockwise from north. The rotation is
* performed about the anchor point. If not specified, the default is 0 (i.e., up on the image
* points north).
* <p/>
* If a ground overlay with position set using {@link #positionFromBounds(LatLngBounds)} is
* rotated, its size will preserved and it will no longer be guaranteed to fit inside the
* bounds.
*
* @param bearing the bearing in degrees clockwise from north. Values outside the range
* [0, 360) will be normalized.
* @return this {@link GroundOverlayOptions} object with a new bearing set.
*/
public GroundOverlayOptions bearing(float bearing) {
this.bearing = bearing;
return this;
}
/**
* Horizontal distance, normalized to [0, 1], of the anchor from the left edge.
*
* @return the u value of the anchor.
*/
public float getAnchorU() {
return anchorU;
}
/**
* Vertical distance, normalized to [0, 1], of the anchor from the top edge.
*
* @return the v value of the anchor.
*/
public float getAnchorV() {
return anchorV;
}
/**
* Gets the bearing set for this options object.
*
* @return the bearing of the ground overlay.
*/
public float getBearing() {
return bearing;
}
/**
* Gets the bounds set for this options object.
*
* @return the bounds of the ground overlay. This will be {@code null} if the position was set
* using {@link #position(LatLng, float)} or {@link #position(LatLng, float, float)}
*/
public LatLngBounds getBounds() {
return bounds;
}
/**
* Gets the height set for this options object.
*
* @return the height of the ground overlay.
*/
public float getHeight() {
return height;
}
/**
* Gets the image set for this options object.
*
* @return the image of the ground overlay.
*/
public BitmapDescriptor getImage() {
if (imageDescriptor == null && image != null) {
imageDescriptor = new BitmapDescriptor(ObjectWrapper.asInterface(image));
}
return imageDescriptor;
}
/**
* Gets the location set for this options object.
*
* @return the location to place the anchor of the ground overlay. This will be {@code null}
* if the position was set using {@link #positionFromBounds(LatLngBounds)}.
*/
public LatLng getLocation() {
return location;
}
/**
* Gets the transparency set for this options object.
*
* @return the transparency of the ground overlay.
*/
public float getTransparency() {
return transparency;
}
/**
* Gets the width set for this options object.
*
* @return the width of the ground overlay.
*/
public float getWidth() {
return width;
}
/**
* Gets the zIndex set for this options object.
*
* @return the zIndex of the ground overlay.
*/
public float getZIndex() {
return zIndex;
}
/**
* Specifies the image for this ground overlay.
* <p/>
* To load an image as a texture (which is used to draw the image on a map), it must be
* converted into an image with sides that are powers of two. This is so that a mipmap can be
* created in order to render the texture at various zoom levels - see
* <a href="http://en.wikipedia.org/wiki/Mipmap">Mipmap (Wikipedia)</a> for details. Hence, to
* conserve memory by avoiding this conversion, it is advised that the dimensions of the image
* are powers of two.
*
* @param image the {@link BitmapDescriptor} to use for this ground overlay
* @return this {@link GroundOverlayOptions} object with a new image set.
*/
public GroundOverlayOptions image(BitmapDescriptor image) {
this.imageDescriptor = image;
this.image = imageDescriptor.getRemoteObject().asBinder();
return this;
}
/**
* Gets the visibility setting for this options object.
*
* @return {@code true} if the ground overlay is to be visible; {@code false} if it is not.
*/
public boolean isVisible() {
return visible;
}
/**
* Specifies the position for this ground overlay using an anchor point (a {@link LatLng}),
* width and height (both in meters). When rendered, the image will be scaled to fit the
* dimensions specified (i.e., its proportions will not necessarily be preserved).
*
* @param location the location on the map {@code LatLng} to which the anchor point in the
* given image will remain fixed. The anchor will remain fixed to the position
* on the ground when transformations are applied (e.g., setDimensions,
* setBearing, etc.).
* @param width the width of the overlay (in meters)
* @param height the height of the overlay (in meters)
* @return this {@link GroundOverlayOptions} object with a new position set.
* @throws IllegalArgumentException if anchor is null
* @throws IllegalArgumentException if width or height are negative
* @throws IllegalStateException if the position was already set using
* {@link #positionFromBounds(LatLngBounds)}
*/
public GroundOverlayOptions position(LatLng location, float width, float height)
throws IllegalArgumentException, IllegalStateException {
position(location, width);
if (height < 0)
throw new IllegalArgumentException("height must not be negative");
this.height = height;
return this;
}
/**
* Specifies the position for this ground overlay using an anchor point (a {@link LatLng}) and
* the width (in meters). When rendered, the image will retain its proportions from the bitmap,
* i.e., the height will be calculated to preserve the original proportions of the image.
*
* @param location the location on the map {@link LatLng} to which the anchor point in the
* given image will remain fixed. The anchor will remain fixed to the position
* on the ground when transformations are applied (e.g., setDimensions,
* setBearing, etc.).
* @param width the width of the overlay (in meters). The height will be determined
* automatically based on the image proportions.
* @return this {@link GroundOverlayOptions} object with a new position set.
* @throws IllegalArgumentException if anchor is null
* @throws IllegalArgumentException if width is negative
* @throws IllegalStateException if the position was already set using
* {@link #positionFromBounds(LatLngBounds)}
*/
public GroundOverlayOptions position(LatLng location, float width)
throws IllegalArgumentException, IllegalStateException {
if (location == null)
throw new IllegalArgumentException("location must not be null");
if (width < 0)
throw new IllegalArgumentException("width must not be negative");
if (bounds != null)
throw new IllegalStateException("Position already set using positionFromBounds()");
this.location = location;
this.width = width;
return this;
}
/**
* Specifies the position for this ground overlay. When rendered, the image will be scaled to
* fit the bounds (i.e., its proportions will not necessarily be preserved).
*
* @param bounds a {@link LatLngBounds} in which to place the ground overlay
* @return this {@link GroundOverlayOptions} object with a new position set.
* @throws IllegalStateException if the position was already set using
* {@link #position(LatLng, float)} or
* {@link #position(LatLng, float, float)}
*/
public GroundOverlayOptions positionFromBounds(LatLngBounds bounds)
throws IllegalStateException {
if (location != null)
throw new IllegalStateException("Position already set using position()");
this.bounds = bounds;
return this;
}
/**
* Specifies the transparency of the ground overlay. The default transparency is {code 0}
* (opaque).
*
* @param transparency a float in the range {@code [0..1]} where {@code 0} means that the
* ground overlay is opaque and {code 1} means that the ground overlay is
* transparent
* @return this {@link GroundOverlayOptions} object with a new visibility setting.
* @throws IllegalArgumentException if the transparency is outside the range [0..1].
*/
public GroundOverlayOptions transparency(float transparency) throws IllegalArgumentException {
if (transparency < 0 || transparency > 1)
throw new IllegalArgumentException("transparency must be in range [0..1]");
this.transparency = transparency;
return this;
}
/**
* Specifies the visibility for the ground overlay. The default visibility is {@code true}.
*
* @return this {@link GroundOverlayOptions} object with a new visibility setting.
*/
public GroundOverlayOptions visible(boolean visible) {
this.visible = visible;
return this;
}
/**
* Specifies the ground overlay's zIndex, i.e., the order in which it will be drawn. See the
* documentation at the top of this class for more information about zIndex.
*
* @return this {@link GroundOverlayOptions} object with a new zIndex set.
*/
public GroundOverlayOptions zIndex(float zIndex) {
this.zIndex = zIndex;
return this;
}
public static Creator<GroundOverlayOptions> CREATOR = new AutoCreator<GroundOverlayOptions>(GroundOverlayOptions.class);
}

View File

@ -1,341 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.maps.model;
import android.os.IBinder;
import com.google.android.gms.dynamic.ObjectWrapper;
import org.microg.gms.common.PublicApi;
import org.microg.safeparcel.AutoSafeParcelable;
import org.microg.safeparcel.SafeParceled;
@PublicApi
public class MarkerOptions extends AutoSafeParcelable {
@SafeParceled(1)
private int versionCode = 1;
@SafeParceled(2)
private LatLng position;
@SafeParceled(3)
private String title;
@SafeParceled(4)
private String snippet;
/**
* This is a IBinder to the remote BitmapDescriptor created using BitmapDescriptorFactory
*/
@SafeParceled(5)
private IBinder iconBinder;
private BitmapDescriptor icon;
@SafeParceled(6)
private float anchorU = 0.5F;
@SafeParceled(7)
private float anchorV = 1F;
@SafeParceled(8)
private boolean draggable;
@SafeParceled(9)
private boolean visible;
@SafeParceled(10)
private boolean flat;
@SafeParceled(11)
private float rotation = 0F;
@SafeParceled(12)
private float infoWindowAnchorU = 0F;
@SafeParceled(13)
private float infoWindowAnchorV = 1F;
@SafeParceled(14)
private float alpha = 1F;
@SafeParceled(15)
private float zIndex = 0F;
/**
* Creates a new set of marker options.
*/
public MarkerOptions() {
}
/**
* Sets the alpha (opacity) of the marker. This is a value from 0 to 1, where 0 means the
* marker is completely transparent and 1 means the marker is completely opaque.
*
* @return the object for which the method was called, with the new alpha set.
*/
public MarkerOptions alpha(float alpha) {
this.alpha = alpha;
return this;
}
/**
* Specifies the anchor to be at a particular point in the marker image.
* <p/>
* The anchor specifies the point in the icon image that is anchored to the marker's position
* on the Earth's surface.
* <p/>
* The anchor point is specified in the continuous space [0.0, 1.0] x [0.0, 1.0], where (0, 0)
* is the top-left corner of the image, and (1, 1) is the bottom-right corner. The anchoring
* point in a W x H image is the nearest discrete grid point in a (W + 1) x (H + 1) grid,
* obtained by scaling the then rounding. For example, in a 4 x 2 image, the anchor point
* (0.7, 0.6) resolves to the grid point at (3, 1).
*
* @param u u-coordinate of the anchor, as a ratio of the image width (in the range [0, 1])
* @param v v-coordinate of the anchor, as a ratio of the image height (in the range [0, 1])
* @return the object for which the method was called, with the new anchor set.
*/
public MarkerOptions anchor(float u, float v) {
this.anchorU = u;
this.anchorV = v;
return this;
}
/**
* Sets the draggability for the marker.
*
* @return the object for which the method was called, with the new draggable state set.
*/
public MarkerOptions draggable(boolean draggable) {
this.draggable = draggable;
return this;
}
/**
* Sets whether this marker should be flat against the map true or a billboard facing the
* camera false. If the marker is flat against the map, it will remain stuck to the map as the
* camera rotates and tilts but will still remain the same size as the camera zooms, unlike a
* GroundOverlay. If the marker is a billboard, it will always be drawn facing the camera
* and will rotate and tilt with the camera. The default value is false.
*
* @return the object for which the method was called, with the new flat state set.
*/
public MarkerOptions flat(boolean flat) {
this.flat = flat;
return this;
}
/**
* Gets the alpha set for this MarkerOptions object.
*
* @return the alpha of the marker in the range [0, 1].
*/
public float getAlpha() {
return alpha;
}
/**
* Horizontal distance, normalized to [0, 1], of the anchor from the left edge.
*
* @return the u value of the anchor.
*/
public float getAnchorU() {
return anchorU;
}
/**
* Vertical distance, normalized to [0, 1], of the anchor from the top edge.
*
* @return the v value of the anchor.
*/
public float getAnchorV() {
return anchorV;
}
/**
* Gets the custom icon set for this MarkerOptions object.
*
* @return An {@link BitmapDescriptor} representing the custom icon, or {@code null} if no
* custom icon is set.
*/
public BitmapDescriptor getIcon() {
if (icon == null && iconBinder != null) {
icon = new BitmapDescriptor(ObjectWrapper.asInterface(iconBinder));
}
return icon;
}
/**
* Horizontal distance, normalized to [0, 1], of the info window anchor from the left edge.
*
* @return the u value of the info window anchor.
*/
public float getInfoWindowAnchorU() {
return infoWindowAnchorU;
}
/**
* Vertical distance, normalized to [0, 1], of the info window anchor from the top edge.
*
* @return the v value of the info window anchor.
*/
public float getInfoWindowAnchorV() {
return infoWindowAnchorV;
}
/**
* Returns the position set for this MarkerOptions object.
*
* @return A {@link LatLng} object specifying the marker's current position.
*/
public LatLng getPosition() {
return position;
}
/**
* Gets the rotation set for this MarkerOptions object.
*
* @return the rotation of the marker in degrees clockwise from the default position.
*/
public float getRotation() {
return rotation;
}
/**
* Gets the snippet set for this MarkerOptions object.
*
* @return A string containing the marker's snippet.
*/
public String getSnippet() {
return snippet;
}
/**
* Gets the title set for this MarkerOptions object.
*
* @return A string containing the marker's title.
*/
public String getTitle() {
return title;
}
/**
* Sets the icon for the marker.
*
* @param icon if null, the default marker is used.
* @return the object for which the method was called, with the new icon set.
*/
public MarkerOptions icon(BitmapDescriptor icon) {
this.icon = icon;
this.iconBinder = icon == null ? null : icon.getRemoteObject().asBinder();
return this;
}
/**
* Specifies the anchor point of the info window on the marker image. This is specified in the
* same coordinate system as the anchor. See {@link MarkerOptions#anchor(float, float)} for
* more details. The default is the top middle of the image.
*
* @param u u-coordinate of the info window anchor, as a ratio of the image width (in the range [0, 1])
* @param v v-coordinate of the info window anchor, as a ratio of the image height (in the range [0, 1])
* @return the object for which the method was called, with the new info window anchor set.
*/
public MarkerOptions infoWindowAnchor(float u, float v) {
this.infoWindowAnchorU = u;
this.infoWindowAnchorV = v;
return this;
}
/**
* Gets the draggability setting for this MarkerOptions object.
*
* @return {@code true} if the marker is draggable; otherwise, returns {@code false}.
*/
public boolean isDraggable() {
return draggable;
}
/**
* Gets the flat setting for this MarkerOptions object.
*
* @return {@code true} if the marker is flat against the map; {@code false} if the marker
* should face the camera.
*/
public boolean isFlat() {
return flat;
}
/**
* Gets the visibility setting for this MarkerOptions object.
*
* @return {@code true} if the marker is visible; otherwise, returns {@code false}.
*/
public boolean isVisible() {
return visible;
}
/**
* Sets the location for the marker.
*
* @return the object for which the method was called, with the new position set.
*/
public MarkerOptions position(LatLng position) {
this.position = position;
return this;
}
/**
* Sets the rotation of the marker in degrees clockwise about the marker's anchor point. The
* axis of rotation is perpendicular to the marker. A rotation of 0 corresponds to the default
* position of the marker. When the marker is flat on the map, the default position is North
* aligned and the rotation is such that the marker always remains flat on the map. When the
* marker is a billboard, the default position is pointing up and the rotation is such that
* the marker is always facing the camera. The default value is 0.
*
* @return the object for which the method was called, with the new rotation set.
*/
public MarkerOptions rotation(float rotation) {
this.rotation = rotation;
return this;
}
/**
* Sets the snippet for the marker.
*
* @return the object for which the method was called, with the new snippet set.
*/
public MarkerOptions snippet(String snippet) {
this.snippet = snippet;
return this;
}
/**
* Sets the title for the marker.
*
* @return the object for which the method was called, with the new title set.
*/
public MarkerOptions title(String title) {
this.title = title;
return this;
}
/**
* Sets the visibility for the marker.
*
* @return the object for which the method was called, with the new visibility state set.
*/
public MarkerOptions visible(boolean visible) {
this.visible = visible;
return this;
}
public MarkerOptions zIndex(float zIndex) {
this.zIndex = zIndex;
return this;
}
public float getZIndex() {
return this.zIndex;
}
public static Creator<MarkerOptions> CREATOR = new AutoCreator<MarkerOptions>(MarkerOptions.class);
}

View File

@ -1,150 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.maps.model;
import android.graphics.Color;
import org.microg.gms.common.PublicApi;
import org.microg.safeparcel.AutoSafeParcelable;
import org.microg.safeparcel.SafeParceled;
import java.util.ArrayList;
import java.util.List;
/**
* Defines options for a polygon.
* TODO: Docs
*/
@PublicApi
public class PolygonOptions extends AutoSafeParcelable {
@SafeParceled(1)
private int versionCode = 1;
@SafeParceled(value = 2, subClass = LatLng.class)
private List<LatLng> points = new ArrayList<LatLng>();
@SafeParceled(value = 3, subClass = LatLng.class, useClassLoader = true)
private List<List<LatLng>> holes = new ArrayList<List<LatLng>>();
@SafeParceled(4)
private float strokeWidth = 10;
@SafeParceled(5)
private int strokeColor = Color.BLACK;
@SafeParceled(6)
private int fillColor = Color.TRANSPARENT;
@SafeParceled(7)
private float zIndex = 0;
@SafeParceled(8)
private boolean visible = true;
@SafeParceled(9)
private boolean geodesic = false;
/**
* Creates polygon options.
*/
public PolygonOptions() {
}
public PolygonOptions add(LatLng point) {
points.add(point);
return this;
}
public PolygonOptions add(LatLng... points) {
for (LatLng point : points) {
this.points.add(point);
}
return this;
}
public PolygonOptions add(Iterable<LatLng> points) {
for (LatLng point : points) {
this.points.add(point);
}
return this;
}
public PolygonOptions addHole(Iterable<LatLng> points) {
ArrayList<LatLng> hole = new ArrayList<LatLng>();
for (LatLng point : points) {
hole.add(point);
}
holes.add(hole);
return this;
}
public PolygonOptions fillColor(int color) {
this.fillColor = color;
return this;
}
public PolygonOptions geodesic(boolean geodesic) {
this.geodesic = geodesic;
return this;
}
public int getFillColor() {
return fillColor;
}
public List<List<LatLng>> getHoles() {
return holes;
}
public List<LatLng> getPoints() {
return points;
}
public int getStrokeColor() {
return strokeColor;
}
public float getStrokeWidth() {
return strokeWidth;
}
public float getZIndex() {
return zIndex;
}
public boolean isGeodesic() {
return geodesic;
}
public boolean isVisible() {
return visible;
}
public PolygonOptions strokeColor(int color) {
this.strokeColor = color;
return this;
}
public PolygonOptions strokeWidth(float width) {
this.strokeWidth = width;
return this;
}
public PolygonOptions visible(boolean visible) {
this.visible = visible;
return this;
}
public PolygonOptions zIndex(float zIndex) {
this.zIndex = zIndex;
return this;
}
public static Creator<PolygonOptions> CREATOR = new AutoCreator<PolygonOptions>(PolygonOptions.class);
}

View File

@ -1,121 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.maps.model;
import android.graphics.Color;
import org.microg.gms.common.PublicApi;
import org.microg.safeparcel.AutoSafeParcelable;
import org.microg.safeparcel.SafeParceled;
import java.util.ArrayList;
import java.util.List;
/**
* Defines options for a polyline.
* TODO
*/
@PublicApi
public class PolylineOptions extends AutoSafeParcelable {
@SafeParceled(1)
private int versionCode = 1;
@SafeParceled(value = 2, subClass = LatLng.class)
private List<LatLng> points = new ArrayList<LatLng>();
@SafeParceled(3)
private float width = 10;
@SafeParceled(4)
private int color = Color.BLACK;
@SafeParceled(5)
private float zIndex = 0;
@SafeParceled(6)
private boolean visible = true;
@SafeParceled(7)
private boolean geodesic = false;
public PolylineOptions() {
}
public PolylineOptions add(LatLng point) {
points.add(point);
return this;
}
public PolylineOptions add(LatLng... points) {
for (LatLng point : points) {
this.points.add(point);
}
return this;
}
public PolylineOptions addAll(Iterable<LatLng> points) {
for (LatLng point : points) {
this.points.add(point);
}
return this;
}
public PolylineOptions color(int color) {
this.color = color;
return this;
}
public PolylineOptions geodesic(boolean geodesic) {
this.geodesic = geodesic;
return this;
}
public int getColor() {
return color;
}
public List<LatLng> getPoints() {
return points;
}
public float getWidth() {
return width;
}
public float getZIndex() {
return zIndex;
}
public boolean isGeodesic() {
return geodesic;
}
public boolean isVisible() {
return visible;
}
public PolylineOptions visible(boolean visible) {
this.visible = visible;
return this;
}
public PolylineOptions width(float width) {
this.width = width;
return this;
}
public PolylineOptions zIndex(float zIndex) {
this.zIndex = zIndex;
return this;
}
public static Creator<PolylineOptions> CREATOR = new AutoCreator<PolylineOptions>(PolylineOptions.class);
}

View File

@ -1,23 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.maps.model;
import org.microg.gms.common.PublicApi;
@PublicApi
public class StreetViewPanoramaCamera {
}

View File

@ -1,23 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.maps.model;
import org.microg.gms.common.PublicApi;
@PublicApi
public class StreetViewPanoramaLink {
}

View File

@ -1,23 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.maps.model;
import org.microg.gms.common.PublicApi;
@PublicApi
public class StreetViewPanoramaLocation {
}

View File

@ -1,23 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.maps.model;
import org.microg.gms.common.PublicApi;
@PublicApi
public class StreetViewPanoramaOrientation {
}

View File

@ -1,69 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.maps.model;
import org.microg.gms.common.PublicApi;
import org.microg.safeparcel.AutoSafeParcelable;
import org.microg.safeparcel.SafeParceled;
/**
* Contains information about a Tile that is returned by a {@link TileProvider}.
* TODO SafeParceled
*/
@PublicApi
public class Tile extends AutoSafeParcelable {
@SafeParceled(1)
private int versionCode = 1;
/**
* The width of the image encoded by {@link #data} in pixels.
*/
@SafeParceled(2)
public final int width;
/**
* The height of the image encoded by {@link #data} in pixels.
*/
@SafeParceled(3)
public final int height;
/**
* A byte array containing the image data. The image will be created from this data by calling
* {@link android.graphics.BitmapFactory#decodeByteArray(byte[], int, int)}.
*/
@SafeParceled(4)
public final byte[] data;
private Tile() {
width = height = 0;
data = null;
}
/**
* Constructs a {@link Tile}.
*
* @param width the width of the image in pixels
* @param height the height of the image in pixels
* @param data A byte array containing the image data. The image will be created from this
* data by calling
* {@link android.graphics.BitmapFactory#decodeByteArray(byte[], int, int)}.
*/
public Tile(int width, int height, byte[] data) {
this.width = width;
this.height = height;
this.data = data;
}
public static Creator<Tile> CREATOR = new AutoCreator<Tile>(Tile.class);
}

View File

@ -1,141 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.maps.model;
import android.os.IBinder;
import android.os.RemoteException;
import com.google.android.gms.maps.model.internal.ITileProviderDelegate;
import org.microg.gms.common.PublicApi;
import org.microg.safeparcel.AutoSafeParcelable;
import org.microg.safeparcel.SafeParceled;
/**
* Defines options for a TileOverlay.
*/
@PublicApi
public class TileOverlayOptions extends AutoSafeParcelable {
@SafeParceled(1)
private int versionCode = 1;
/**
* This is a IBinder to the {@link #tileProvider}, built using {@link ITileProviderDelegate}.
*/
@SafeParceled(2)
private IBinder tileProviderBinder;
private TileProvider tileProvider;
@SafeParceled(3)
private boolean visible = true;
@SafeParceled(4)
private float zIndex;
@SafeParceled(5)
private boolean fadeIn = true;
/**
* Creates a new set of tile overlay options.
*/
public TileOverlayOptions() {
}
/**
* Specifies whether the tiles should fade in. The default is {@code true}.
*
* @return this {@link TileOverlayOptions} object with a new fadeIn setting.
*/
public TileOverlayOptions fadeIn(boolean fadeIn) {
this.fadeIn = fadeIn;
return this;
}
/**
* Gets whether the tiles should fade in.
*
* @return {@code true} if the tiles are to fade in; {@code false} if it is not.
*/
public boolean getFadeIn() {
return fadeIn;
}
/**
* Gets the tile provider set for this {@link TileOverlayOptions} object.
*
* @return the {@link TileProvider} of the tile overlay.
*/
public TileProvider getTileProvider() {
return tileProvider;
}
/**
* Gets the zIndex set for this {@link TileOverlayOptions} object.
*
* @return the zIndex of the tile overlay.
*/
public float getZIndex() {
return zIndex;
}
/**
* Gets the visibility setting for this {@link TileOverlayOptions} object.
*
* @return {@code true} if the tile overlay is to be visible; {@code false} if it is not.
*/
public boolean isVisible() {
return visible;
}
/**
* Specifies the tile provider to use for this tile overlay.
*
* @param tileProvider the {@link TileProvider} to use for this tile overlay.
* @return the object for which the method was called, with the new tile provider set.
*/
public TileOverlayOptions tileProvider(final TileProvider tileProvider) {
this.tileProvider = tileProvider;
this.tileProviderBinder = new ITileProviderDelegate.Stub() {
@Override
public Tile getTile(int x, int y, int zoom) throws RemoteException {
return tileProvider.getTile(x, y, zoom);
}
};
return this;
}
/**
* Specifies the visibility for the tile overlay. The default visibility is {@code true}.
*
* @return this {@link TileOverlayOptions} object with a new visibility setting.
*/
public TileOverlayOptions visible(boolean visible) {
this.visible = visible;
return this;
}
/**
* Specifies the tile overlay's zIndex, i.e., the order in which it will be drawn where
* overlays with larger values are drawn above those with lower values. See the documentation
* at the top of this class for more information about zIndex.
*
* @return this {@link TileOverlayOptions} object with a new zIndex set.
*/
public TileOverlayOptions zIndex(float zIndex) {
this.zIndex = zIndex;
return this;
}
public static Creator<TileOverlayOptions> CREATOR = new AutoCreator<TileOverlayOptions>(TileOverlayOptions.class);
}

View File

@ -1,41 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.maps.model;
/**
* An interface for a class that provides the tile images for a TileOverlay. For information about
* the tile coordinate system, see TileOverlay.
* <p/>
* Calls to methods in this interface might be made from multiple threads so implementations of
* this interface must be threadsafe.
*/
public interface TileProvider {
public static final Tile NO_TILE = new Tile(-1, -1, null);
/**
* Returns the tile to be used for this tile coordinate.
*
* @param x The x coordinate of the tile. This will be in the range [0, 2^(zoom - 1)] inclusive.
* @param y The y coordinate of the tile. This will be in the range [0, 2^(zoom - 1)] inclusive.
* @param zoom The zoom level of the tile.
* @return the {@link Tile} to be used for this tile coordinate. If you do not wish to provide
* a tile for this tile coordinate, return {@link #NO_TILE}. If the tile could not be found at
* this point in time, return null and further requests might be made with an exponential
* backoff.
*/
public Tile getTile(int x, int y, int zoom);
}

View File

@ -1,67 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.maps.model;
import org.microg.gms.common.PublicApi;
import org.microg.safeparcel.AutoSafeParcelable;
import org.microg.safeparcel.SafeParceled;
@PublicApi
public class VisibleRegion extends AutoSafeParcelable {
@SafeParceled(1)
private int versionCode;
@SafeParceled(2)
private LatLng nearLeft;
@SafeParceled(3)
private LatLng nearRight;
@SafeParceled(4)
private LatLng farLeft;
@SafeParceled(5)
private LatLng farRight;
@SafeParceled(6)
private LatLngBounds bounds;
private VisibleRegion() {
}
public VisibleRegion(int versionCode, LatLng nearLeft, LatLng nearRight, LatLng farLeft,
LatLng farRight, LatLngBounds bounds) {
this.versionCode = versionCode;
this.nearLeft = nearLeft;
this.nearRight = nearRight;
this.farLeft = farLeft;
this.farRight = farRight;
this.bounds = bounds;
}
public VisibleRegion(LatLng nearLeft, LatLng nearRight, LatLng farLeft, LatLng farRight,
LatLngBounds bounds) {
this(1, nearLeft, nearRight, farLeft, farRight, bounds);
}
/**
* This is assuming that the visible region matches the bounds, which means that it's a north
* orientated top view
*/
public VisibleRegion(LatLngBounds bounds) {
this(bounds.southwest, new LatLng(bounds.southwest.latitude, bounds.northeast.longitude),
new LatLng(bounds.northeast.latitude, bounds.southwest.longitude), bounds.northeast,
bounds);
}
public static Creator<VisibleRegion> CREATOR = new AutoCreator<VisibleRegion>(VisibleRegion.class);
}

View File

@ -1,20 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Contains the Google Maps Android API model classes.
*/
package com.google.android.gms.maps.model;

View File

@ -36,28 +36,15 @@ dependencies {
api "uk.uuid.slf4j:slf4j-android:1.7.25-1"
implementation project(':play-services-base-core')
implementation project(':play-services-location-core')
implementation project(':play-services-core:microg-ui-tools') // deprecated
implementation project(':play-services-api')
implementation project(':play-services-cast-api')
implementation project(':play-services-wearable')
implementation "org.microg:wearable:$wearableVersion"
implementation "org.microg.gms:remote-droid-guard:$remoteDroidGuardVersion"
mapboxImplementation project(':play-services-maps-core-mapbox')
vtmImplementation project(':play-services-maps-core-vtm')
// AndroidX UI
implementation "androidx.multidex:multidex:$multidexVersion"
implementation "androidx.appcompat:appcompat:$appcompatVersion"
implementation "androidx.mediarouter:mediarouter:$mediarouterVersion"
implementation "androidx.preference:preference:$preferenceVersion"
// Navigation
implementation "androidx.navigation:navigation-fragment:$navigationVersion"
implementation "androidx.navigation:navigation-ui:$navigationVersion"
implementation "androidx.navigation:navigation-fragment-ktx:$navigationVersion"
implementation "androidx.navigation:navigation-ui-ktx:$navigationVersion"
}
android {

View File

@ -36,26 +36,6 @@
android:name="com.mgoogle.android.googleapps.permission.GOOGLE_AUTH"
android:icon="@drawable/proprietary_auth_ic_scope_icon_default" />
<permission
android:name="com.mgoogle.android.googleapps.permission.GOOGLE_AUTH.cp"
android:description="@string/permission_service_cp_description"
android:label="@string/permission_service_cp_label"
android:protectionLevel="dangerous" />
<permission
android:name="com.mgoogle.android.googleapps.permission.GOOGLE_AUTH.local"
android:description="@string/permission_service_local_description"
android:label="@string/permission_service_local_label"
android:protectionLevel="dangerous" />
<permission
android:name="com.mgoogle.android.googleapps.permission.GOOGLE_AUTH.mail"
android:description="@string/permission_service_mail_description"
android:label="@string/permission_service_mail_label"
android:protectionLevel="dangerous" />
<permission
android:name="com.mgoogle.android.googleapps.permission.GOOGLE_AUTH.writely"
android:description="@string/permission_service_writely_description"
android:label="@string/permission_service_writely_label"
android:protectionLevel="dangerous" />
<permission
android:name="org.mgoogle.gms.STATUS_BROADCAST"
android:label="@string/perm_status_broadcast_label"
@ -67,12 +47,7 @@
<uses-permission android:name="android.permission.FAKE_PACKAGE_SIGNATURE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
@ -111,47 +86,6 @@
android:name="fake-signature"
android:value="@string/fake_signature" />
<!-- Location -->
<activity android:name="org.microg.nlp.ui.BackendSettingsActivity"
android:theme="@style/Theme.AppCompat.DayNight" />
<activity
android:name="org.microg.gms.ui.PlacePickerActivity"
android:exported="true"
android:label="@string/pick_place_title"
android:theme="@style/Theme.AppCompat.DayNight.NoActionBar">
<intent-filter>
<action android:name="com.google.android.gms.location.places.ui.PICK_PLACE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<service android:name="org.microg.gms.places.GeoDataService">
<intent-filter>
<action android:name="com.google.android.gms.location.places.GeoDataApi" />
<action android:name="com.google.android.gms.location.places.PlacesApi" />
</intent-filter>
</service>
<service android:name="org.microg.gms.places.PlaceDetectionService">
<intent-filter>
<action android:name="com.google.android.gms.location.places.PlaceDetectionApi" />
</intent-filter>
</service>
<service
android:name="org.microg.gms.wearable.location.WearableLocationService">
<intent-filter>
<action android:name="com.google.android.gms.wearable.MESSAGE_RECEIVED" />
<data
android:host="*"
android:pathPrefix="/com/google/android/location/fused/wearable"
android:scheme="wear" />
</intent-filter>
</service>
<!-- Services Framework -->
<provider
@ -297,13 +231,6 @@
android:resource="@xml/contact_syncadapter" />
</service>
<!-- Wearable -->
<service android:name="org.microg.gms.wearable.WearableService">
<intent-filter>
<action android:name="com.google.android.gms.wearable.BIND" />
</intent-filter>
</service>
<!-- Auth -->
@ -475,16 +402,6 @@
android:label="@string/gms_settings_name"
android:theme="@style/Theme.AppCompat.DayNight" />
<activity
android:name="org.microg.gms.ui.SafetyNetFragment$AsActivity"
android:label="@string/service_name_snet"
android:theme="@style/Theme.AppCompat.DayNight" />
<activity
android:name="org.microg.gms.ui.SafetyNetAdvancedFragment$AsActivity"
android:label="@string/service_name_snet"
android:theme="@style/Theme.AppCompat.DayNight" />
<activity
android:name="org.microg.gms.ui.SelfCheckFragment$AsActivity"
android:label="@string/self_check_title"
@ -607,11 +524,6 @@
</intent-filter>
</service>
<service android:name="org.microg.gms.snet.SafetyNetClientService">
<intent-filter>
<action android:name="com.google.android.gms.safetynet.service.START" />
</intent-filter>
</service>
<service android:name="org.microg.gms.wallet.PaymentService">
<intent-filter>
<action android:name="com.google.android.gms.wallet.service.BIND" />

View File

@ -1,200 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.snet;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.util.Base64;
import android.util.Log;
import com.squareup.wire.Wire;
import org.microg.gms.common.Build;
import org.microg.gms.common.Constants;
import org.microg.gms.common.Utils;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.zip.GZIPInputStream;
import okio.ByteString;
public class Attestation {
private static final String TAG = "GmsSafetyNetAttest";
private Context context;
private String packageName;
private byte[] payload;
private String droidGaurdResult;
public Attestation(Context context, String packageName) {
this.context = context;
this.packageName = packageName;
}
public void setPayload(byte[] payload) {
this.payload = payload;
}
public byte[] buildPayload(byte[] nonce) {
this.droidGaurdResult = null;
SafetyNetData payload = new SafetyNetData.Builder()
.nonce(ByteString.of(nonce))
.currentTimeMs(System.currentTimeMillis())
.packageName(packageName)
.fileDigest(getPackageFileDigest())
.signatureDigest(getPackageSignatures())
.gmsVersionCode(Constants.MAX_REFERENCE_VERSION)
//.googleCn(false)
.seLinuxState(new SELinuxState(true, true))
.suCandidates(Collections.<FileState>emptyList())
.build();
return this.payload = payload.toByteArray();
}
public byte[] getPayload() {
return payload;
}
public String getPayloadHashBase64() {
try {
MessageDigest digest = getSha256Digest();
return Base64.encodeToString(digest.digest(payload), Base64.NO_WRAP);
} catch (NoSuchAlgorithmException e) {
Log.w(TAG, e);
return null;
}
}
private static MessageDigest getSha256Digest() throws NoSuchAlgorithmException {
return MessageDigest.getInstance("SHA-256");
}
public void setDroidGaurdResult(String droidGaurdResult) {
this.droidGaurdResult = droidGaurdResult;
}
private ByteString getPackageFileDigest() {
try {
FileInputStream is = new FileInputStream(new File(context.getPackageManager().getApplicationInfo(packageName, 0).sourceDir));
MessageDigest digest = getSha256Digest();
byte[] data = new byte[16384];
while (true) {
int read = is.read(data);
if (read < 0) break;
digest.update(data, 0, read);
}
return ByteString.of(digest.digest());
} catch (Exception e) {
Log.w(TAG, e);
return null;
}
}
@SuppressLint("PackageManagerGetSignatures")
private List<ByteString> getPackageSignatures() {
try {
PackageInfo pi = context.getPackageManager().getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
ArrayList<ByteString> res = new ArrayList<>();
MessageDigest digest = getSha256Digest();
for (Signature signature : pi.signatures) {
res.add(ByteString.of(digest.digest(signature.toByteArray())));
}
return res;
} catch (Exception e) {
Log.w(TAG, e);
return null;
}
}
public String attest(String apiKey) throws IOException {
if (payload == null) {
throw new IllegalStateException("missing payload");
}
return attest(new AttestRequest(ByteString.of(payload), droidGaurdResult), apiKey).result;
}
private AttestResponse attest(AttestRequest request, String apiKey) throws IOException {
String requestUrl = SafetyNetPrefs.get(context).getServiceUrl() + "?alt=PROTO&key=" + apiKey;
HttpURLConnection connection = (HttpURLConnection) new URL(requestUrl).openConnection();
connection.setRequestMethod("POST");
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setRequestProperty("content-type", "application/x-protobuf");
connection.setRequestProperty("Accept-Encoding", "gzip");
Build build = Utils.getBuild(context);
connection.setRequestProperty("User-Agent", "SafetyNet/" + Constants.MAX_REFERENCE_VERSION + " (" + build.device + " " + build.id + "); gzip");
OutputStream os = connection.getOutputStream();
os.write(request.toByteArray());
os.close();
if (connection.getResponseCode() != 200) {
byte[] bytes = null;
String ex = null;
try {
bytes = Utils.readStreamToEnd(connection.getErrorStream());
ex = new String(Utils.readStreamToEnd(new GZIPInputStream(new ByteArrayInputStream(bytes))));
} catch (Exception e) {
if (bytes != null) {
throw new IOException(getBytesAsString(bytes), e);
}
throw new IOException(connection.getResponseMessage(), e);
}
throw new IOException(ex);
}
InputStream is = connection.getInputStream();
byte[] bytes = Utils.readStreamToEnd(new GZIPInputStream(is));
try {
return new Wire().parseFrom(bytes, AttestResponse.class);
} catch (IOException e) {
Log.d(TAG, Base64.encodeToString(bytes, 0));
throw e;
}
}
private String getBytesAsString(byte[] bytes) {
if (bytes == null) return "null";
try {
CharsetDecoder d = Charset.forName("US-ASCII").newDecoder();
CharBuffer r = d.decode(ByteBuffer.wrap(bytes));
return r.toString();
} catch (Exception e) {
return Base64.encodeToString(bytes, Base64.NO_WRAP);
}
}
}

View File

@ -1,37 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.snet;
import android.os.RemoteException;
import com.google.android.gms.common.internal.GetServiceRequest;
import com.google.android.gms.common.internal.IGmsCallbacks;
import org.microg.gms.BaseService;
import org.microg.gms.common.GmsService;
public class SafetyNetClientService extends BaseService {
public SafetyNetClientService() {
super("GmsSafetyNetClientSvc", GmsService.SAFETY_NET_CLIENT);
}
@Override
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException {
callback.onPostInitComplete(0, new SafetyNetClientServiceImpl(this, request.packageName), null);
}
}

View File

@ -1,136 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.snet;
import android.content.Context;
import android.os.Bundle;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.Base64;
import android.util.Log;
import com.google.android.gms.common.api.CommonStatusCodes;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.safetynet.AttestationData;
import com.google.android.gms.safetynet.HarmfulAppsData;
import com.google.android.gms.safetynet.internal.ISafetyNetCallbacks;
import com.google.android.gms.safetynet.internal.ISafetyNetService;
import org.microg.gms.checkin.LastCheckinInfo;
import org.microg.gms.common.PackageUtils;
import org.microg.gms.droidguard.RemoteDroidGuardConnector;
import java.io.IOException;
import java.util.ArrayList;
public class SafetyNetClientServiceImpl extends ISafetyNetService.Stub {
private static final String TAG = "GmsSafetyNetClientImpl";
private static final String DEFAULT_API_KEY = "AIzaSyDqVnJBjE5ymo--oBJt3On7HQx9xNm1RHA";
private Context context;
private String packageName;
private Attestation attestation;
public SafetyNetClientServiceImpl(Context context, String packageName) {
this.context = context;
this.packageName = packageName;
this.attestation = new Attestation(context, packageName);
}
@Override
public void attest(ISafetyNetCallbacks callbacks, byte[] nonce) throws RemoteException {
attestWithApiKey(callbacks, nonce, DEFAULT_API_KEY);
}
@Override
public void attestWithApiKey(final ISafetyNetCallbacks callbacks, final byte[] nonce, String apiKey) throws RemoteException {
if (nonce == null) {
callbacks.onAttestationData(new Status(CommonStatusCodes.DEVELOPER_ERROR), null);
return;
}
if (!SafetyNetPrefs.get(context).isEnabled()) {
Log.d(TAG, "ignoring SafetyNet request, it's disabled");
callbacks.onAttestationData(Status.CANCELED, null);
return;
}
new Thread(new Runnable() {
@Override
public void run() {
try {
try {
attestation.buildPayload(nonce);
RemoteDroidGuardConnector conn = new RemoteDroidGuardConnector(context);
Bundle bundle = new Bundle();
bundle.putString("contentBinding", attestation.getPayloadHashBase64());
RemoteDroidGuardConnector.Result dg = conn.guard("attest", Long.toString(LastCheckinInfo.read(context).androidId), bundle);
if (!SafetyNetPrefs.get(context).isOfficial() || dg != null && dg.getStatusCode() == 0 && dg.getResult() != null) {
if (dg != null && dg.getStatusCode() == 0 && dg.getResult() != null) {
attestation.setDroidGaurdResult(Base64.encodeToString(dg.getResult(), Base64.NO_WRAP + Base64.NO_PADDING + Base64.URL_SAFE));
}
AttestationData data = new AttestationData(attestation.attest(apiKey));
callbacks.onAttestationData(Status.SUCCESS, data);
} else {
callbacks.onAttestationData(dg == null ? Status.INTERNAL_ERROR : new Status(dg.getStatusCode()), null);
}
} catch (IOException e) {
Log.w(TAG, e);
callbacks.onAttestationData(Status.INTERNAL_ERROR, null);
}
} catch (RemoteException e) {
Log.w(TAG, e);
}
}
}).start();
}
@Override
public void getSharedUuid(ISafetyNetCallbacks callbacks) throws RemoteException {
PackageUtils.checkPackageUid(context, packageName, getCallingUid());
PackageUtils.assertExtendedAccess(context);
// TODO
Log.d(TAG, "dummy Method: getSharedUuid");
callbacks.onString("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa");
}
@Override
public void lookupUri(ISafetyNetCallbacks callbacks, String s1, int[] threatTypes, int i, String s2) throws RemoteException {
Log.d(TAG, "unimplemented Method: lookupUri");
}
@Override
public void init(ISafetyNetCallbacks callbacks) throws RemoteException {
Log.d(TAG, "dummy Method: init");
callbacks.onBoolean(Status.SUCCESS, true);
}
@Override
public void getHarmfulAppsList(ISafetyNetCallbacks callbacks) throws RemoteException {
Log.d(TAG, "dummy Method: unknown4");
callbacks.onHarmfulAppsData(Status.SUCCESS, new ArrayList<HarmfulAppsData>());
}
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
if (super.onTransact(code, data, reply, flags)) return true;
Log.d(TAG, "onTransact [unknown]: " + code + ", " + data + ", " + flags);
return false;
}
}

View File

@ -1,99 +0,0 @@
/*
* Copyright (C) 2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.snet;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
public class SafetyNetPrefs implements SharedPreferences.OnSharedPreferenceChangeListener {
private static final String OFFICIAL_ATTEST_BASE_URL = "https://www.googleapis.com/androidcheck/v1/attestations/attest";
public static final String PREF_SNET_DISABLED = "snet_disabled";
public static final String PREF_SNET_OFFICIAL = "snet_official";
public static final String PREF_SNET_THIRD_PARTY = "snet_third_party";
public static final String PREF_SNET_CUSTOM_URL = "snet_custom_url";
public static final String PREF_SNET_SELF_SIGNED = "snet_self_signed";
private static SafetyNetPrefs INSTANCE;
public static SafetyNetPrefs get(Context context) {
if (INSTANCE == null) {
if (context == null) return new SafetyNetPrefs(null);
INSTANCE = new SafetyNetPrefs(context.getApplicationContext());
}
return INSTANCE;
}
private boolean disabled;
private boolean official;
private boolean selfSigned;
private boolean thirdParty;
private String customUrl;
private SharedPreferences defaultPreferences;
private SafetyNetPrefs(Context context) {
if (context != null) {
defaultPreferences = PreferenceManager.getDefaultSharedPreferences(context);
defaultPreferences.registerOnSharedPreferenceChangeListener(this);
update();
}
}
public void update() {
disabled = defaultPreferences.getBoolean(PREF_SNET_DISABLED, true);
official = defaultPreferences.getBoolean(PREF_SNET_OFFICIAL, false);
selfSigned = defaultPreferences.getBoolean(PREF_SNET_SELF_SIGNED, false);
thirdParty = defaultPreferences.getBoolean(PREF_SNET_THIRD_PARTY, false);
customUrl = defaultPreferences.getString(PREF_SNET_CUSTOM_URL, null);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
update();
}
public boolean isEnabled() {
return !disabled && (official || selfSigned || thirdParty);
}
public void setEnabled(boolean enabled) {
defaultPreferences.edit().putBoolean(PREF_SNET_DISABLED, !enabled).apply();
if (enabled && !isEnabled()) {
official = true;
defaultPreferences.edit().putBoolean(PREF_SNET_OFFICIAL, true).apply();
}
}
public boolean isSelfSigned() {
return selfSigned;
}
public boolean isOfficial() {
return official;
}
public boolean isThirdParty() {
return thirdParty;
}
public String getServiceUrl() {
if (official) return OFFICIAL_ATTEST_BASE_URL;
return customUrl;
}
}

View File

@ -1,254 +0,0 @@
/*
* Copyright (C) 2013-2019 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.ui;
import android.content.Intent;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationManager;
import android.os.Build;
import android.os.Bundle;
import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.SearchView;
import androidx.appcompat.widget.Toolbar;
import androidx.core.app.ActivityCompat;
import androidx.core.view.MenuItemCompat;
import com.mgoogle.android.gms.R;
import com.google.android.gms.common.api.CommonStatusCodes;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.location.places.internal.PlaceImpl;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;
import org.microg.gms.location.LocationConstants;
//import org.microg.gms.maps.vtm.BackendMapView;
//import org.microg.gms.maps.vtm.GmsMapsTypeHelper;
import org.microg.safeparcel.SafeParcelUtil;
//import org.oscim.core.MapPosition;
//import org.oscim.event.Event;
//import org.oscim.map.Map;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static org.microg.gms.location.LocationConstants.EXTRA_PRIMARY_COLOR;
import static org.microg.gms.location.LocationConstants.EXTRA_PRIMARY_COLOR_DARK;
//import static org.microg.gms.maps.vtm.GmsMapsTypeHelper.fromLatLngBounds;
public class
PlacePickerActivity extends AppCompatActivity /*implements Map.UpdateListener*/ {
private static final String TAG = "GmsPlacePicker";
private PlaceImpl place;
// private BackendMapView mapView;
private Intent resultIntent;
private AtomicBoolean geocoderInProgress = new AtomicBoolean(false);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
resultIntent = new Intent();
place = new PlaceImpl();
setContentView(R.layout.pick_place);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
if (getIntent().hasExtra(EXTRA_PRIMARY_COLOR)) {
toolbar.setBackgroundColor(getIntent().getIntExtra(EXTRA_PRIMARY_COLOR, 0));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
getWindow().setStatusBarColor(getIntent().getIntExtra(EXTRA_PRIMARY_COLOR_DARK, 0));
((TextView) findViewById(R.id.place_picker_title)).setTextColor(getIntent().getIntExtra(EXTRA_PRIMARY_COLOR_DARK, 0));
}
// mapView = (BackendMapView) findViewById(R.id.map);
// mapView.map().getEventLayer().enableRotation(false);
// mapView.map().getEventLayer().enableTilt(false);
// mapView.map().events.bind(this);
LatLngBounds latLngBounds = getIntent().getParcelableExtra(LocationConstants.EXTRA_BOUNDS);
if (latLngBounds != null) {
place.viewport = latLngBounds;
// MapPosition mp = new MapPosition();
// mp.setByBoundingBox(fromLatLngBounds(latLngBounds), mapView.map().getWidth(), mapView.map().getHeight());
// mapView.map().getMapPosition(mp);
} else {
if (ActivityCompat.checkSelfPermission(PlacePickerActivity.this, ACCESS_FINE_LOCATION) != PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(PlacePickerActivity.this, new String[]{ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}, 0);
} else {
updateMapFromLocationManager();
}
}
findViewById(R.id.place_picker_select).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
resultIntent.putExtra(LocationConstants.EXTRA_STATUS, SafeParcelUtil.asByteArray(new Status(CommonStatusCodes.SUCCESS)));
resultIntent.putExtra(LocationConstants.EXTRA_PLACE, SafeParcelUtil.asByteArray(place));
resultIntent.putExtra(LocationConstants.EXTRA_FINAL_BOUNDS, SafeParcelUtil.asByteArray(place.viewport));
setResult(RESULT_OK, resultIntent);
finish();
}
});
}
@SuppressWarnings("MissingPermission")
private void updateMapFromLocationManager() {
LocationManager lm = (LocationManager) getSystemService(LOCATION_SERVICE);
Location last = null;
for (String provider : lm.getAllProviders()) {
if (lm.isProviderEnabled(provider)) {
Location t = lm.getLastKnownLocation(provider);
if (t != null && (last == null || t.getTime() > last.getTime())) {
last = t;
}
}
}
Log.d(TAG, "Set location to " + last);
if (last != null) {
// mapView.map().setMapPosition(new MapPosition(last.getLatitude(), last.getLongitude(), 4096));
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == 0) {
for (int grantResult : grantResults) {
if (grantResult != PERMISSION_GRANTED) return;
}
updateMapFromLocationManager();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.pick_place, menu);
SearchView searchView = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.menu_action_search));
// TODO: search
return true;
}
@Override
protected void onResume() {
super.onResume();
// mapView.onResume();
}
@Override
protected void onPause() {
// mapView.onPause();
super.onPause();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
protected void onDestroy() {
super.onDestroy();
}
/*
@Override
public void onMapEvent(Event event, MapPosition position) {
// place.viewport = GmsMapsTypeHelper.toLatLngBounds(mapView.map().viewport().getBBox(null, 0));
// resultIntent.putExtra(LocationConstants.EXTRA_FINAL_BOUNDS, place.viewport);
// place.latLng = GmsMapsTypeHelper.toLatLng(position.getGeoPoint());
place.name = "";
place.address = "";
updateInfoText();
if (geocoderInProgress.compareAndSet(false, true)) {
new Thread(new Runnable() {
@Override
public void run() {
try {
LatLng ll = null;
while (ll != place.latLng) {
ll = place.latLng;
Thread.sleep(1000);
}
Geocoder geocoder = new Geocoder(PlacePickerActivity.this);
List<Address> addresses = geocoder.getFromLocation(place.latLng.latitude, place.latLng.longitude, 1);
if (addresses != null && !addresses.isEmpty() && addresses.get(0).getMaxAddressLineIndex() > 0) {
Address address = addresses.get(0);
StringBuilder sb = new StringBuilder(address.getAddressLine(0));
for (int i = 1; i < address.getMaxAddressLineIndex(); ++i) {
if (i == 1 && sb.toString().equals(address.getFeatureName())) {
sb = new StringBuilder(address.getAddressLine(i));
continue;
}
sb.append(", ").append(address.getAddressLine(i));
}
if (place.latLng == ll) {
place.address = sb.toString();
place.name = address.getFeatureName();
runOnUiThread(new Runnable() {
@Override
public void run() {
updateInfoText();
}
});
}
}
} catch (Exception ignored) {
Log.w(TAG, ignored);
} finally {
geocoderInProgress.lazySet(false);
}
}
}).start();
}
}*/
private void updateInfoText() {
if (TextUtils.isEmpty(place.address)) {
((TextView) findViewById(R.id.place_picker_info)).setText(getString(R.string.place_picker_location_lat_lng, place.latLng.latitude, place.latLng.longitude));
} else if (TextUtils.isEmpty(place.name)) {
((TextView) findViewById(R.id.place_picker_info)).setText(place.address);
} else {
((TextView) findViewById(R.id.place_picker_info)).setText(Html.fromHtml("<b>" + place.name + "</b>, " + place.address));
}
}
}

View File

@ -1,85 +0,0 @@
/*
* Copyright (C) 2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.ui;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.preference.Preference;
import com.mgoogle.android.gms.R;
import org.microg.tools.ui.AbstractSettingsActivity;
import org.microg.tools.ui.RadioButtonPreference;
import org.microg.tools.ui.ResourceSettingsFragment;
import static org.microg.gms.snet.SafetyNetPrefs.PREF_SNET_OFFICIAL;
import static org.microg.gms.snet.SafetyNetPrefs.PREF_SNET_SELF_SIGNED;
import static org.microg.gms.snet.SafetyNetPrefs.PREF_SNET_THIRD_PARTY;
public class SafetyNetAdvancedFragment extends ResourceSettingsFragment {
public SafetyNetAdvancedFragment() {
preferencesResource = R.xml.preferences_snet_advanced;
}
private RadioButtonPreference radioOfficial;
private RadioButtonPreference radioSelfSigned;
private RadioButtonPreference radioThirdParty;
@Override
public void onCreatePreferences(@Nullable Bundle savedInstanceState, String rootKey) {
super.onCreatePreferences(savedInstanceState, rootKey);
radioOfficial = (RadioButtonPreference) findPreference(PREF_SNET_OFFICIAL);
radioSelfSigned = (RadioButtonPreference) findPreference(PREF_SNET_SELF_SIGNED);
radioThirdParty = (RadioButtonPreference) findPreference(PREF_SNET_THIRD_PARTY);
}
@Override
public boolean onPreferenceTreeClick(Preference preference) {
if (preference == radioOfficial) {
radioOfficial.setChecked(true);
radioSelfSigned.setChecked(false);
radioThirdParty.setChecked(false);
return true;
} else if (preference == radioSelfSigned) {
radioOfficial.setChecked(false);
radioSelfSigned.setChecked(true);
radioThirdParty.setChecked(false);
return true;
} else if (preference == radioThirdParty) {
radioOfficial.setChecked(false);
radioSelfSigned.setChecked(false);
radioThirdParty.setChecked(true);
return true;
}
return super.onPreferenceTreeClick(preference);
}
public static class AsActivity extends AbstractSettingsActivity {
public AsActivity() {
showHomeAsUp = true;
}
@Override
protected Fragment getFragment() {
return new SafetyNetAdvancedFragment();
}
}
}

View File

@ -1,82 +0,0 @@
/*
* Copyright (C) 2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.ui;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.mgoogle.android.gms.R;
import org.microg.gms.snet.SafetyNetPrefs;
import org.microg.tools.ui.AbstractSettingsActivity;
import org.microg.tools.ui.SwitchBarResourceSettingsFragment;
public class SafetyNetFragment extends SwitchBarResourceSettingsFragment {
private final int MENU_ADVANCED = Menu.FIRST;
public SafetyNetFragment() {
preferencesResource = R.xml.preferences_snet;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setHasOptionsMenu(true);
switchBar.setChecked(SafetyNetPrefs.get(getContext()).isEnabled());
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.add(0, MENU_ADVANCED, 0, R.string.menu_advanced);
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_ADVANCED:
Intent intent = new Intent(getContext(), SafetyNetAdvancedFragment.AsActivity.class);
startActivity(intent);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onSwitchBarChanged(boolean isChecked) {
SafetyNetPrefs.get(getContext()).setEnabled(isChecked);
}
public static class AsActivity extends AbstractSettingsActivity {
public AsActivity() {
showHomeAsUp = true;
}
@Override
protected Fragment getFragment() {
return new SafetyNetFragment();
}
}
}

View File

@ -27,7 +27,6 @@ import com.mgoogle.android.gms.R;
import org.microg.gms.gcm.GcmDatabase;
import org.microg.gms.gcm.GcmPrefs;
import org.microg.gms.snet.SafetyNetPrefs;
//import org.microg.nlp.Preferences;
import org.microg.tools.ui.AbstractDashboardActivity;
import org.microg.tools.ui.ResourceSettingsFragment;
@ -82,21 +81,7 @@ public class SettingsActivity extends AbstractDashboardActivity {
findPreference(PREF_GCM).setSummary(R.string.abc_capital_off);
}
if (SafetyNetPrefs.get(getContext()).isEnabled()) {
String snet_info = "";
if (SafetyNetPrefs.get(getContext()).isOfficial()) {
snet_info = getString(R.string.pref_snet_status_official_info);
} else if (SafetyNetPrefs.get(getContext()).isSelfSigned()) {
snet_info = getString(R.string.pref_snet_status_self_signed_info);
} else if (SafetyNetPrefs.get(getContext()).isThirdParty()) {
snet_info = getString(R.string.pref_snet_status_third_party_info);
}
findPreference(PREF_SNET).setSummary(getString(R.string.service_status_enabled) + " / " + snet_info);
} else {
findPreference(PREF_SNET).setSummary(R.string.service_status_disabled);
}
// Preferences unifiedNlPrefs = new Preferences(getContext());
// int backendCount = TextUtils.isEmpty(unifiedNlPrefs.getLocationBackends()) ? 0 :

View File

@ -2,8 +2,6 @@ package org.microg.gms.ui;
import androidx.fragment.app.Fragment;
import org.microg.nlp.ui.BackendDetailsFragment;
import org.microg.nlp.ui.BackendListFragment;
import org.microg.tools.ui.AbstractSettingsActivity;
public class UnifiedBackendDetailsActivity extends AbstractSettingsActivity {
@ -13,6 +11,6 @@ public class UnifiedBackendDetailsActivity extends AbstractSettingsActivity {
@Override
protected Fragment getFragment() {
return new BackendDetailsFragment();
return null;
}
}

View File

@ -8,7 +8,6 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import org.microg.nlp.ui.BackendListFragment;
import org.microg.tools.ui.AbstractSettingsActivity;
public class UnifiedBackendListActivity extends AppCompatActivity {
@ -21,7 +20,7 @@ public class UnifiedBackendListActivity extends AppCompatActivity {
} catch (Exception e) {
Log.w("GmsCoreSettingUi", e);
}
getSupportFragmentManager().beginTransaction().add(new BackendListFragment(), null).commit();
getSupportFragmentManager().beginTransaction().add(null, null).commit();
}
@Override

View File

@ -1,85 +0,0 @@
/*
* Copyright (C) 2019 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.wearable;
import android.content.Context;
import android.net.Uri;
import com.google.android.gms.common.api.CommonStatusCodes;
import com.google.android.gms.common.data.DataHolder;
import com.google.android.gms.wearable.Node;
import com.google.android.gms.wearable.WearableStatusCodes;
import com.google.android.gms.wearable.internal.NodeParcelable;
import org.microg.gms.common.PackageUtils;
import java.util.HashSet;
import java.util.Set;
public class CapabilityManager {
private static final Uri ROOT = Uri.parse("wear:/capabilities/");
private final Context context;
private final WearableImpl wearable;
private final String packageName;
private Set<String> capabilities = new HashSet<String>();
public CapabilityManager(Context context, WearableImpl wearable, String packageName) {
this.context = context;
this.wearable = wearable;
this.packageName = packageName;
}
private Uri buildCapabilityUri(String capability, boolean withAuthority) {
Uri.Builder builder = ROOT.buildUpon();
if (withAuthority) builder.authority(wearable.getLocalNodeId());
builder.appendPath(packageName);
builder.appendPath(PackageUtils.firstSignatureDigest(context, packageName));
builder.appendPath(Uri.encode(capability));
return builder.build();
}
public Set<String> getNodesForCapability(String capability) {
DataHolder dataHolder = wearable.getDataItemsByUriAsHolder(buildCapabilityUri(capability, false), packageName);
Set<String> nodes = new HashSet<>();
for (int i = 0; i < dataHolder.getCount(); i++) {
nodes.add(dataHolder.getString("host", i, 0));
}
dataHolder.close();
return nodes;
}
public int add(String capability) {
if (this.capabilities.contains(capability)) {
return WearableStatusCodes.DUPLICATE_CAPABILITY;
}
DataItemInternal dataItem = new DataItemInternal(buildCapabilityUri(capability, true));
DataItemRecord record = wearable.putDataItem(packageName, PackageUtils.firstSignatureDigest(context, packageName), wearable.getLocalNodeId(), dataItem);
this.capabilities.add(capability);
wearable.syncRecordToAll(record);
return CommonStatusCodes.SUCCESS;
}
public int remove(String capability) {
if (!this.capabilities.contains(capability)) {
return WearableStatusCodes.UNKNOWN_CAPABILITY;
}
wearable.deleteDataItems(buildCapabilityUri(capability, true), packageName);
capabilities.remove(capability);
return CommonStatusCodes.SUCCESS;
}
}

View File

@ -1,62 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.wearable;
import android.content.Context;
import android.content.SharedPreferences;
import java.util.UUID;
public class ClockworkNodePreferences {
private static final String CLOCKWORK_NODE_PREFERENCES = "cw_node";
private static final String CLOCKWORK_NODE_PREFERENCE_NODE_ID = "node_id";
private static final String CLOCKWORK_NODE_PREFERENCE_NEXT_SEQ_ID_BLOCK = "nextSeqIdBlock";
private static final Object lock = new Object();
private static long seqIdBlock;
private static long seqIdInBlock = -1;
private Context context;
public ClockworkNodePreferences(Context context) {
this.context = context;
}
public String getLocalNodeId() {
SharedPreferences preferences = context.getSharedPreferences(CLOCKWORK_NODE_PREFERENCES, Context.MODE_PRIVATE);
String nodeId = preferences.getString(CLOCKWORK_NODE_PREFERENCE_NODE_ID, null);
if (nodeId == null) {
nodeId = UUID.randomUUID().toString();
preferences.edit().putString(CLOCKWORK_NODE_PREFERENCE_NODE_ID, nodeId).apply();
}
return nodeId;
}
public long getNextSeqId() {
synchronized (lock) {
SharedPreferences preferences = context.getSharedPreferences(CLOCKWORK_NODE_PREFERENCES, Context.MODE_PRIVATE);
if (seqIdInBlock < 0) seqIdInBlock = 1000;
if (seqIdInBlock >= 1000) {
seqIdBlock = preferences.getLong(CLOCKWORK_NODE_PREFERENCE_NEXT_SEQ_ID_BLOCK, 100);
preferences.edit().putLong(CLOCKWORK_NODE_PREFERENCE_NEXT_SEQ_ID_BLOCK, seqIdBlock + seqIdInBlock).apply();
seqIdInBlock = 0;
}
return seqIdBlock + seqIdInBlock++;
}
}
}

View File

@ -1,124 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.wearable;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import com.google.android.gms.wearable.ConnectionConfiguration;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class ConfigurationDatabaseHelper extends SQLiteOpenHelper {
public static final String NULL_STRING = "NULL_STRING";
public static final String TABLE_NAME = "connectionConfigurations";
public static final String BY_NAME = "name=?";
public ConfigurationDatabaseHelper(Context context) {
super(context, "connectionconfig.db", null, 2);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE connectionConfigurations (_id INTEGER PRIMARY KEY AUTOINCREMENT,androidId TEXT,name TEXT NOT NULL,pairedBtAddress TEXT NOT NULL,connectionType INTEGER NOT NULL,role INTEGER NOT NULL,connectionEnabled INTEGER NOT NULL,nodeId TEXT, UNIQUE(name) ON CONFLICT REPLACE);");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
private static ConnectionConfiguration configFromCursor(final Cursor cursor) {
String name = cursor.getString(cursor.getColumnIndex("name"));
String pairedBtAddress = cursor.getString(cursor.getColumnIndex("pairedBtAddress"));
int connectionType = cursor.getInt(cursor.getColumnIndex("connectionType"));
int role = cursor.getInt(cursor.getColumnIndex("role"));
int enabled = cursor.getInt(cursor.getColumnIndex("connectionEnabled"));
String nodeId = cursor.getString(cursor.getColumnIndex("nodeId"));
if (NULL_STRING.equals(name)) name = null;
if (NULL_STRING.equals(pairedBtAddress)) pairedBtAddress = null;
return new ConnectionConfiguration(name, pairedBtAddress, connectionType, role, enabled > 0, nodeId);
}
public ConnectionConfiguration getConfiguration(String name) {
Cursor cursor = getReadableDatabase().query(TABLE_NAME, null, BY_NAME, new String[]{name}, null, null, null);
ConnectionConfiguration config = null;
if (cursor != null) {
if (cursor.moveToNext())
config = configFromCursor(cursor);
cursor.close();
}
return config;
}
public void putConfiguration(ConnectionConfiguration config) {
putConfiguration(config, null);
}
public void putConfiguration(ConnectionConfiguration config, String oldNodeId) {
ContentValues contentValues = new ContentValues();
if (config.name != null) {
contentValues.put("name", config.name);
} else if (config.role == 2) {
contentValues.put("name", "server");
} else {
contentValues.put("name", "NULL_STRING");
}
if (config.address != null) {
contentValues.put("pairedBtAddress", config.address);
} else {
contentValues.put("pairedBtAddress", "NULL_STRING");
}
contentValues.put("connectionType", config.type);
contentValues.put("role", config.role);
contentValues.put("connectionEnabled", true);
contentValues.put("nodeId", config.nodeId);
if (oldNodeId == null) {
getWritableDatabase().insert(TABLE_NAME, null, contentValues);
} else {
getWritableDatabase().update(TABLE_NAME, contentValues, "nodeId=?", new String[]{oldNodeId});
}
}
public ConnectionConfiguration[] getAllConfigurations() {
Cursor cursor = getReadableDatabase().query(TABLE_NAME, null, null, null, null, null, null);
if (cursor != null) {
List<ConnectionConfiguration> configurations = new ArrayList<ConnectionConfiguration>();
while (cursor.moveToNext()) {
configurations.add(configFromCursor(cursor));
}
cursor.close();
return configurations.toArray(new ConnectionConfiguration[configurations.size()]);
} else {
return null;
}
}
public void setEnabledState(String name, boolean enabled) {
getWritableDatabase().execSQL("UPDATE connectionConfigurations SET connectionEnabled=? WHERE name=?", new String[]{enabled ? "1" : "0", name});
}
public int deleteConfiguration(String name) {
return getWritableDatabase().delete(TABLE_NAME, BY_NAME, new String[]{name});
}
}

View File

@ -1,63 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.wearable;
import android.net.Uri;
import com.google.android.gms.wearable.Asset;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class DataItemInternal {
public final String host;
public final String path;
public final Uri uri;
public byte[] data;
private Map<String, Asset> assets = new HashMap<String, Asset>();
public DataItemInternal(String host, String path) {
this.host = host;
this.path = path;
this.uri = new Uri.Builder().scheme("wear").authority(host).path(path).build();
}
public DataItemInternal(Uri uri) {
this.uri = uri;
this.host = uri.getAuthority();
this.path = uri.getPath();
}
public DataItemInternal addAsset(String key, Asset asset) {
this.assets.put(key, asset);
return this;
}
public Map<String, Asset> getAssets() {
return Collections.unmodifiableMap(new HashMap<String, Asset>(assets));
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("DataItemInternal{");
sb.append("uri=").append(uri);
sb.append(", assets=").append(assets.size());
sb.append('}');
return sb.toString();
}
}

View File

@ -1,188 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.wearable;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.util.Log;
import com.google.android.gms.common.data.DataHolder;
import com.google.android.gms.wearable.Asset;
import com.google.android.gms.wearable.internal.DataItemAssetParcelable;
import com.google.android.gms.wearable.internal.DataItemParcelable;
import org.microg.wearable.proto.AssetEntry;
import org.microg.wearable.proto.SetDataItem;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import okio.ByteString;
public class DataItemRecord {
private static String[] EVENT_DATA_HOLDER_FIELDS = new String[] { "event_type", "path", "data", "tags", "asset_key", "asset_id" };
public DataItemInternal dataItem;
public String source;
public long seqId;
public long v1SeqId;
public long lastModified;
public boolean deleted;
public boolean assetsAreReady;
public String packageName;
public String signatureDigest;
public ContentValues toContentValues() {
ContentValues contentValues = new ContentValues();
contentValues.put("sourceNode", source);
contentValues.put("seqId", seqId);
contentValues.put("v1SourceNode", source);
contentValues.put("v1SeqId", v1SeqId);
contentValues.put("timestampMs", lastModified);
if (deleted) {
contentValues.put("deleted", 1);
contentValues.putNull("data");
} else {
contentValues.put("deleted", 0);
contentValues.put("data", dataItem.data);
}
contentValues.put("assetsPresent", assetsAreReady ? 1 : 0);
return contentValues;
}
public DataHolder toEventDataHolder() {
DataHolder.Builder builder = DataHolder.builder(EVENT_DATA_HOLDER_FIELDS);
HashMap<String, Object> data = new HashMap<String, Object>();
data.put("path", dataItem.uri.toString());
if (deleted) {
data.put("event_type", 2);
builder.withRow(data);
} else {
data.put("event_type", 1);
data.put("data", dataItem.data);
data.put("tags", "");
boolean added = false;
for (Map.Entry<String, Asset> entry : dataItem.getAssets().entrySet()) {
added = true;
data.put("asset_id", entry.getValue().getDigest());
data.put("asset_key", entry.getKey());
builder.withRow(data);
data = new HashMap<String, Object>();
data.put("path", dataItem.uri.toString());
}
if (!added) {
builder.withRow(data);
}
}
return builder.build(0);
}
public DataItemParcelable toParcelable() {
Map<String, DataItemAssetParcelable> assets = new HashMap<>();
for (Map.Entry<String, Asset> entry : dataItem.getAssets().entrySet()) {
assets.put(entry.getKey(), new DataItemAssetParcelable(entry.getValue().getDigest(), entry.getKey()));
}
DataItemParcelable parcelable = new DataItemParcelable(dataItem.uri, assets);
parcelable.data = dataItem.data;
return parcelable;
}
public SetDataItem toSetDataItem() {
SetDataItem.Builder builder = new SetDataItem.Builder()
.packageName(packageName)
.signatureDigest(signatureDigest)
.uri(dataItem.uri.toString())
.seqId(seqId)
.deleted(deleted)
.lastModified(lastModified);
if (source != null) builder.source(source);
if (dataItem.data != null) builder.data(ByteString.of(dataItem.data));
List<AssetEntry> protoAssets = new ArrayList<AssetEntry>();
Map<String, Asset> assets = dataItem.getAssets();
for (String key : assets.keySet()) {
protoAssets.add(new AssetEntry.Builder()
.key(key)
.unknown3(4)
.value(new org.microg.wearable.proto.Asset.Builder()
.digest(assets.get(key).getDigest())
.build()).build());
}
builder.assets(protoAssets);
return builder.build();
}
public static DataItemRecord fromCursor(Cursor cursor) {
DataItemRecord record = new DataItemRecord();
record.packageName = cursor.getString(1);
record.signatureDigest = cursor.getString(2);
record.dataItem = new DataItemInternal(cursor.getString(3), cursor.getString(4));
record.seqId = cursor.getLong(5);
record.deleted = cursor.getLong(6) > 0;
record.source = cursor.getString(7);
record.dataItem.data = cursor.getBlob(8);
record.lastModified = cursor.getLong(9);
record.assetsAreReady = cursor.getLong(10) > 0;
if (cursor.getString(11) != null) {
record.dataItem.addAsset(cursor.getString(11), Asset.createFromRef(cursor.getString(12)));
while (cursor.moveToNext()) {
if (cursor.getLong(5) == record.seqId) {
record.dataItem.addAsset(cursor.getString(11), Asset.createFromRef(cursor.getString(12)));
}
}
cursor.moveToPrevious();
}
return record;
}
public static DataItemRecord fromSetDataItem(SetDataItem setDataItem) {
DataItemRecord record = new DataItemRecord();
record.dataItem = new DataItemInternal(Uri.parse(setDataItem.uri));
if (setDataItem.data != null) record.dataItem.data = setDataItem.data.toByteArray();
if (setDataItem.assets != null) {
for (AssetEntry asset : setDataItem.assets) {
record.dataItem.addAsset(asset.key, Asset.createFromRef(asset.value.digest));
}
}
record.source = setDataItem.source;
record.seqId = setDataItem.seqId;
record.v1SeqId = -1;
record.lastModified = setDataItem.lastModified;
record.deleted = setDataItem.deleted == null ? false : setDataItem.deleted;
record.packageName = setDataItem.packageName;
record.signatureDigest = setDataItem.signatureDigest;
return record;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("DataItemRecord{");
sb.append("dataItem=").append(dataItem);
sb.append(", source='").append(source).append('\'');
sb.append(", seqId=").append(seqId);
sb.append(", v1SeqId=").append(v1SeqId);
sb.append(", lastModified=").append(lastModified);
sb.append(", deleted=").append(deleted);
sb.append(", assetsAreReady=").append(assetsAreReady);
sb.append(", packageName='").append(packageName).append('\'');
sb.append(", signatureDigest='").append(signatureDigest).append('\'');
sb.append('}');
return sb.toString();
}
}

View File

@ -1,179 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.wearable;
import android.text.TextUtils;
import android.util.Log;
import com.google.android.gms.wearable.Asset;
import com.google.android.gms.wearable.ConnectionConfiguration;
import com.google.android.gms.wearable.internal.MessageEventParcelable;
import org.microg.gms.checkin.LastCheckinInfo;
import org.microg.gms.common.Build;
import org.microg.wearable.ServerMessageListener;
import org.microg.wearable.proto.AckAsset;
import org.microg.wearable.proto.Connect;
import org.microg.wearable.proto.FetchAsset;
import org.microg.wearable.proto.FilePiece;
import org.microg.wearable.proto.Heartbeat;
import org.microg.wearable.proto.Request;
import org.microg.wearable.proto.RootMessage;
import org.microg.wearable.proto.SetAsset;
import org.microg.wearable.proto.SetDataItem;
import org.microg.wearable.proto.SyncStart;
import org.microg.wearable.proto.SyncTableEntry;
import java.io.IOException;
import java.util.Arrays;
public class MessageHandler extends ServerMessageListener {
private static final String TAG = "GmsWearMsgHandler";
private final WearableImpl wearable;
private final String oldConfigNodeId;
private String peerNodeId;
public MessageHandler(WearableImpl wearable, ConnectionConfiguration config) {
this(wearable, config, new Build().model, config.nodeId, LastCheckinInfo.read(wearable.getContext()).androidId);
}
private MessageHandler(WearableImpl wearable, ConnectionConfiguration config, String name, String networkId, long androidId) {
super(new Connect.Builder()
.name(name)
.id(wearable.getLocalNodeId())
.networkId(networkId)
.peerAndroidId(androidId)
.unknown4(3)
.peerVersion(1)
.build());
this.wearable = wearable;
this.oldConfigNodeId = config.nodeId;
}
@Override
public void onConnect(Connect connect) {
super.onConnect(connect);
peerNodeId = connect.id;
wearable.onConnectReceived(getConnection(), oldConfigNodeId, connect);
try {
getConnection().writeMessage(new RootMessage.Builder().syncStart(new SyncStart.Builder()
.receivedSeqId(-1L)
.version(2)
.syncTable(Arrays.asList(
new SyncTableEntry.Builder().key("cloud").value(1L).build(),
new SyncTableEntry.Builder().key(wearable.getLocalNodeId()).value(wearable.getCurrentSeqId(wearable.getLocalNodeId())).build(), // TODO
new SyncTableEntry.Builder().key(peerNodeId).value(wearable.getCurrentSeqId(peerNodeId)).build() // TODO
)).build()).build());
} catch (IOException e) {
Log.w(TAG, e);
}
}
@Override
public void onDisconnected() {
Connect connect = getRemoteConnect();
if (connect == null)
connect = new Connect.Builder().id(oldConfigNodeId).name("Wear device").build();
wearable.onDisconnectReceived(getConnection(), connect);
super.onDisconnected();
}
@Override
public void onSetAsset(SetAsset setAsset) {
Log.d(TAG, "onSetAsset: " + setAsset);
Asset asset;
if (setAsset.data != null) {
asset = Asset.createFromBytes(setAsset.data.toByteArray());
} else {
asset = Asset.createFromRef(setAsset.digest);
}
wearable.addAssetToDatabase(asset, setAsset.appkeys.appKeys);
}
@Override
public void onAckAsset(AckAsset ackAsset) {
Log.d(TAG, "onAckAsset: " + ackAsset);
}
@Override
public void onFetchAsset(FetchAsset fetchAsset) {
Log.d(TAG, "onFetchAsset: " + fetchAsset);
}
@Override
public void onSyncStart(SyncStart syncStart) {
Log.d(TAG, "onSyncStart: " + syncStart);
if (syncStart.version < 2) {
Log.d(TAG, "Sync uses version " + syncStart.version + " which is not supported (yet)");
}
boolean hasLocalNode = false;
if (syncStart.syncTable != null) {
for (SyncTableEntry entry : syncStart.syncTable) {
wearable.syncToPeer(peerNodeId, entry.key, entry.value);
if (wearable.getLocalNodeId().equals(entry.key)) hasLocalNode = true;
}
} else {
Log.d(TAG, "No sync table given.");
}
if (!hasLocalNode) wearable.syncToPeer(peerNodeId, wearable.getLocalNodeId(), 0);
}
@Override
public void onSetDataItem(SetDataItem setDataItem) {
Log.d(TAG, "onSetDataItem: " + setDataItem);
wearable.putDataItem(DataItemRecord.fromSetDataItem(setDataItem));
}
@Override
public void onRpcRequest(Request rpcRequest) {
Log.d(TAG, "onRpcRequest: " + rpcRequest);
if (TextUtils.isEmpty(rpcRequest.targetNodeId) || rpcRequest.targetNodeId.equals(wearable.getLocalNodeId())) {
MessageEventParcelable messageEvent = new MessageEventParcelable();
messageEvent.data = rpcRequest.rawData != null ? rpcRequest.rawData.toByteArray() : null;
messageEvent.path = rpcRequest.path;
messageEvent.requestId = rpcRequest.requestId + 31 * (rpcRequest.generation + 527);
messageEvent.sourceNodeId = TextUtils.isEmpty(rpcRequest.sourceNodeId) ? peerNodeId : rpcRequest.sourceNodeId;
wearable.sendMessageReceived(rpcRequest.packageName, messageEvent);
} else if (rpcRequest.targetNodeId.equals(peerNodeId)) {
// Drop it
} else {
// TODO: find next hop
}
try {
getConnection().writeMessage(new RootMessage.Builder().heartbeat(new Heartbeat()).build());
} catch (IOException e) {
onDisconnected();
}
}
@Override
public void onHeartbeat(Heartbeat heartbeat) {
Log.d(TAG, "onHeartbeat: " + heartbeat);
}
@Override
public void onFilePiece(FilePiece filePiece) {
Log.d(TAG, "onFilePiece: " + filePiece);
wearable.handleFilePiece(getConnection(), filePiece.fileName, filePiece.piece.toByteArray(), filePiece.finalPiece ? filePiece.digest : null);
}
@Override
public void onChannelRequest(Request channelRequest) {
Log.d(TAG, "onChannelRequest:" + channelRequest);
}
}

View File

@ -1,297 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.wearable;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.text.TextUtils;
import android.util.Log;
import com.google.android.gms.wearable.Asset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class NodeDatabaseHelper extends SQLiteOpenHelper {
private static final String TAG = "GmsWearNodeDB";
private static final String DB_NAME = "node.db";
private static final String[] GDIBHAP_FIELDS = new String[]{"dataitems_id", "packageName", "signatureDigest", "host", "path", "seqId", "deleted", "sourceNode", "data", "timestampMs", "assetsPresent", "assetname", "assets_digest", "v1SourceNode", "v1SeqId"};
private static final int VERSION = 9;
private ClockworkNodePreferences clockworkNodePreferences;
public NodeDatabaseHelper(Context context) {
super(context, DB_NAME, null, VERSION);
clockworkNodePreferences = new ClockworkNodePreferences(context);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE appkeys(_id INTEGER PRIMARY KEY AUTOINCREMENT,packageName TEXT NOT NULL,signatureDigest TEXT NOT NULL);");
db.execSQL("CREATE TABLE dataitems(_id INTEGER PRIMARY KEY AUTOINCREMENT, appkeys_id INTEGER NOT NULL REFERENCES appkeys(_id), host TEXT NOT NULL, path TEXT NOT NULL, seqId INTEGER NOT NULL, deleted INTEGER NOT NULL, sourceNode TEXT NOT NULL, data BLOB, timestampMs INTEGER NOT NULL, assetsPresent INTEGER NOT NULL, v1SourceNode TEXT NOT NULL, v1SeqId INTEGER NOT NULL);");
db.execSQL("CREATE TABLE assets(digest TEXT PRIMARY KEY, dataPresent INTEGER NOT NULL DEFAULT 0, timestampMs INTEGER NOT NULL);");
db.execSQL("CREATE TABLE assetrefs(assetname TEXT NOT NULL, dataitems_id INTEGER NOT NULL REFERENCES dataitems(_id), assets_digest TEXT NOT NULL REFERENCES assets(digest));");
db.execSQL("CREATE TABLE assetsacls(appkeys_id INTEGER NOT NULL REFERENCES appkeys(_id), assets_digest TEXT NOT NULL);");
db.execSQL("CREATE TABLE nodeinfo(node TEXT NOT NULL PRIMARY KEY, seqId INTEGER, lastActivityMs INTEGER);");
db.execSQL("CREATE VIEW appKeyDataItems AS SELECT appkeys._id AS appkeys_id, appkeys.packageName AS packageName, appkeys.signatureDigest AS signatureDigest, dataitems._id AS dataitems_id, dataitems.host AS host, dataitems.path AS path, dataitems.seqId AS seqId, dataitems.deleted AS deleted, dataitems.sourceNode AS sourceNode, dataitems.data AS data, dataitems.timestampMs AS timestampMs, dataitems.assetsPresent AS assetsPresent, dataitems.v1SourceNode AS v1SourceNode, dataitems.v1SeqId AS v1SeqId FROM appkeys, dataitems WHERE appkeys._id=dataitems.appkeys_id");
db.execSQL("CREATE VIEW appKeyAcls AS SELECT appkeys._id AS appkeys_id, appkeys.packageName AS packageName, appkeys.signatureDigest AS signatureDigest, assetsacls.assets_digest AS assets_digest FROM appkeys, assetsacls WHERE _id=appkeys_id");
db.execSQL("CREATE VIEW dataItemsAndAssets AS SELECT appKeyDataItems.packageName AS packageName, appKeyDataItems.signatureDigest AS signatureDigest, appKeyDataItems.dataitems_id AS dataitems_id, appKeyDataItems.host AS host, appKeyDataItems.path AS path, appKeyDataItems.seqId AS seqId, appKeyDataItems.deleted AS deleted, appKeyDataItems.sourceNode AS sourceNode, appKeyDataItems.data AS data, appKeyDataItems.timestampMs AS timestampMs, appKeyDataItems.assetsPresent AS assetsPresent, assetrefs.assetname AS assetname, assetrefs.assets_digest AS assets_digest, appKeyDataItems.v1SourceNode AS v1SourceNode, appKeyDataItems.v1SeqId AS v1SeqId FROM appKeyDataItems LEFT OUTER JOIN assetrefs ON appKeyDataItems.dataitems_id=assetrefs.dataitems_id");
db.execSQL("CREATE VIEW assetsReadyStatus AS SELECT dataitems_id AS dataitems_id, COUNT(*) = SUM(dataPresent) AS nowReady, assetsPresent AS markedReady FROM assetrefs, dataitems LEFT OUTER JOIN assets ON assetrefs.assets_digest = assets.digest WHERE assetrefs.dataitems_id=dataitems._id GROUP BY dataitems_id;");
db.execSQL("CREATE UNIQUE INDEX appkeys_NAME_AND_SIG ON appkeys(packageName,signatureDigest);");
db.execSQL("CREATE UNIQUE INDEX assetrefs_ASSET_REFS ON assetrefs(assets_digest,dataitems_id,assetname);");
db.execSQL("CREATE UNIQUE INDEX assets_DIGEST ON assets(digest);");
db.execSQL("CREATE UNIQUE INDEX assetsacls_APPKEY_AND_DIGEST ON assetsacls(appkeys_id,assets_digest);");
db.execSQL("CREATE UNIQUE INDEX dataitems_APPKEY_HOST_AND_PATH ON dataitems(appkeys_id,host,path);");
db.execSQL("CREATE UNIQUE INDEX dataitems_SOURCENODE_AND_SEQID ON dataitems(sourceNode,seqId);");
db.execSQL("CREATE UNIQUE INDEX dataitems_SOURCENODE_DELETED_AND_SEQID ON dataitems(sourceNode,deleted,seqId);");
}
public synchronized Cursor getDataItemsForDataHolder(String packageName, String signatureDigest) {
return getDataItemsForDataHolderByHostAndPath(packageName, signatureDigest, null, null);
}
public synchronized Cursor getDataItemsForDataHolderByHostAndPath(String packageName, String signatureDigest, String host, String path) {
String[] params;
String selection;
if (path == null) {
params = new String[]{packageName, signatureDigest};
selection = "packageName = ? AND signatureDigest = ?";
} else if (TextUtils.isEmpty(host)) {
if (path.endsWith("/")) path = path + "%";
path = path.replace("*", "%");
params = new String[]{packageName, signatureDigest, path};
selection = "packageName = ? AND signatureDigest = ? AND path LIKE ?";
} else {
if (path.endsWith("/")) path = path + "%";
path = path.replace("*", "%");
host = host.replace("*", "%");
params = new String[]{packageName, signatureDigest, host, path};
selection = "packageName = ? AND signatureDigest = ? AND host = ? AND path LIKE ?";
}
selection += " AND deleted=0 AND assetsPresent !=0";
return getReadableDatabase().rawQuery("SELECT host AS host,path AS path,data AS data,\'\' AS tags,assetname AS asset_key,assets_digest AS asset_id FROM dataItemsAndAssets WHERE " + selection, params);
}
public synchronized Cursor getDataItemsByHostAndPath(String packageName, String signatureDigest, String host, String path) {
Log.d(TAG, "getDataItemsByHostAndPath: " + packageName + ", " + signatureDigest + ", " + host + ", " + path);
return getDataItemsByHostAndPath(getReadableDatabase(), packageName, signatureDigest, host, path);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion != VERSION) {
// TODO: Upgrade not supported, cleaning up
db.execSQL("DROP TABLE IF EXISTS appkeys;");
db.execSQL("DROP TABLE IF EXISTS dataitems;");
db.execSQL("DROP TABLE IF EXISTS assets;");
db.execSQL("DROP TABLE IF EXISTS assetrefs;");
db.execSQL("DROP TABLE IF EXISTS assetsacls;");
db.execSQL("DROP TABLE IF EXISTS nodeinfo;");
db.execSQL("DROP VIEW IF EXISTS appKeyDataItems;");
db.execSQL("DROP VIEW IF EXISTS appKeyAcls;");
db.execSQL("DROP VIEW IF EXISTS dataItemsAndAssets;");
db.execSQL("DROP VIEW IF EXISTS assetsReadyStatus;");
onCreate(db);
}
}
private static synchronized long getAppKey(SQLiteDatabase db, String packageName, String signatureDigest) {
Cursor cursor = db.rawQuery("SELECT _id FROM appkeys WHERE packageName=? AND signatureDigest=?", new String[]{packageName, signatureDigest});
if (cursor != null) {
try {
if (cursor.moveToNext()) {
return cursor.getLong(0);
}
} finally {
cursor.close();
}
}
ContentValues appKey = new ContentValues();
appKey.put("packageName", packageName);
appKey.put("signatureDigest", signatureDigest);
return db.insert("appkeys", null, appKey);
}
public synchronized void putRecord(DataItemRecord record) {
SQLiteDatabase db = getWritableDatabase();
db.beginTransaction();
Cursor cursor = getDataItemsByHostAndPath(db, record.packageName, record.signatureDigest, record.dataItem.host, record.dataItem.path);
try {
String key;
if (cursor.moveToNext()) {
// update
key = cursor.getString(0);
updateRecord(db, key, record);
} else {
// insert
key = insertRecord(db, record);
}
if (record.assetsAreReady) {
ContentValues update = new ContentValues();
update.put("assetsPresent", 1);
db.update("dataitems", update, "_id=?", new String[]{key});
}
db.setTransactionSuccessful();
} finally {
cursor.close();
}
db.endTransaction();
}
private static void updateRecord(SQLiteDatabase db, String key, DataItemRecord record) {
ContentValues cv = record.toContentValues();
db.update("dataitems", cv, "_id=?", new String[]{key});
finishRecord(db, key, record);
}
private static String insertRecord(SQLiteDatabase db, DataItemRecord record) {
ContentValues contentValues = record.toContentValues();
contentValues.put("appkeys_id", getAppKey(db, record.packageName, record.signatureDigest));
contentValues.put("host", record.dataItem.host);
contentValues.put("path", record.dataItem.path);
String key = Long.toString(db.insertWithOnConflict("dataitems", "host", contentValues, SQLiteDatabase.CONFLICT_REPLACE));
return finishRecord(db, key, record);
}
private static String finishRecord(SQLiteDatabase db, String key, DataItemRecord record) {
if (!record.deleted) {
for (Map.Entry<String, Asset> asset : record.dataItem.getAssets().entrySet()) {
ContentValues assetValues = new ContentValues();
assetValues.put("assets_digest", asset.getValue().getDigest());
assetValues.put("dataitems_id", key);
assetValues.put("assetname", asset.getKey());
db.insertWithOnConflict("assetrefs", "assetname", assetValues, SQLiteDatabase.CONFLICT_IGNORE);
}
Cursor status = db.query("assetsReadyStatus", new String[]{"nowReady"}, "dataitems_id=?", new String[]{key}, null, null, null);
if (status.moveToNext()) {
record.assetsAreReady = status.getLong(0) != 0;
}
status.close();
} else {
record.assetsAreReady = false;
}
return key;
}
private static Cursor getDataItemsByHostAndPath(SQLiteDatabase db, String packageName, String signatureDigest, String host, String path) {
String[] params;
String selection;
if (path == null) {
params = new String[]{packageName, signatureDigest};
selection = "packageName =? AND signatureDigest =?";
} else if (host == null) {
params = new String[]{packageName, signatureDigest, path};
selection = "packageName =? AND signatureDigest =? AND path =?";
} else {
params = new String[]{packageName, signatureDigest, host, path};
selection = "packageName =? AND signatureDigest =? AND host =? AND path =?";
}
selection += " AND deleted=0";
return db.query("dataItemsAndAssets", GDIBHAP_FIELDS, selection, params, null, null, "packageName, signatureDigest, host, path");
}
public Cursor getModifiedDataItems(final String nodeId, final long seqId, final boolean excludeDeleted) {
String selection = "sourceNode =? AND seqId >?" + (excludeDeleted ? " AND deleted =0" : "");
return getReadableDatabase().query("dataItemsAndAssets", GDIBHAP_FIELDS, selection, new String[]{nodeId, Long.toString(seqId)}, null, null, "seqId", null);
}
public synchronized List<DataItemRecord> deleteDataItems(String packageName, String signatureDigest, String host, String path) {
List<DataItemRecord> updated = new ArrayList<DataItemRecord>();
SQLiteDatabase db = getWritableDatabase();
db.beginTransaction();
Cursor cursor = getDataItemsByHostAndPath(db, packageName, signatureDigest, host, path);
while (cursor.moveToNext()) {
DataItemRecord record = DataItemRecord.fromCursor(cursor);
record.deleted = true;
record.assetsAreReady = true;
record.dataItem.data = null;
record.seqId = clockworkNodePreferences.getNextSeqId();
record.v1SeqId = record.seqId;
updateRecord(db, cursor.getString(0), record);
updated.add(record);
}
db.setTransactionSuccessful();
db.endTransaction();
return updated;
}
public long getCurrentSeqId(String sourceNode) {
if (TextUtils.isEmpty(sourceNode)) return 1;
return getCurrentSeqId(getReadableDatabase(), sourceNode);
}
private long getCurrentSeqId(SQLiteDatabase db, String sourceNode) {
Cursor cursor = db.query("dataItemsAndAssets", new String[]{"seqId"}, "sourceNode=?", new String[]{sourceNode}, null, null, "seqId DESC", "1");
long res = 1;
if (cursor != null) {
if (cursor.moveToFirst()) {
res = cursor.getLong(0);
}
cursor.close();
}
return res;
}
public synchronized void putAsset(Asset asset, boolean dataPresent) {
ContentValues cv = new ContentValues();
cv.put("digest", asset.getDigest());
cv.put("dataPresent", dataPresent ? 1 : 0);
cv.put("timestampMs", System.currentTimeMillis());
getWritableDatabase().insertWithOnConflict("assets", null, cv, SQLiteDatabase.CONFLICT_REPLACE);
}
public synchronized void allowAssetAccess(String digest, String packageName, String signatureDigest) {
SQLiteDatabase db = getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put("assets_digest", digest);
cv.put("appkeys_id", getAppKey(db, packageName, signatureDigest));
db.insertWithOnConflict("assetsacls", null, cv, SQLiteDatabase.CONFLICT_REPLACE);
}
public Cursor listMissingAssets() {
return getReadableDatabase().query("dataItemsAndAssets", GDIBHAP_FIELDS, "assetsPresent = 0 AND assets_digest NOT NULL", null, null, null, "packageName, signatureDigest, host, path");
}
public boolean hasAsset(Asset asset) {
Cursor cursor = getReadableDatabase().query("assets", new String[]{"dataPresent"}, "digest=?", new String[]{asset.getDigest()}, null, null, null);
if (cursor == null) return false;
try {
return (cursor.moveToNext() && cursor.getInt(0) == 1);
} finally {
cursor.close();
}
}
public synchronized void markAssetAsPresent(String digest) {
ContentValues cv = new ContentValues();
cv.put("dataPresent", 1);
SQLiteDatabase db = getWritableDatabase();
db.update("assets", cv, "digest=?", new String[]{digest});
Cursor status = db.query("assetsReadyStatus", null, "nowReady != markedReady", null, null, null, null);
while (status.moveToNext()) {
cv = new ContentValues();
cv.put("assetsPresent", status.getInt(status.getColumnIndex("nowReady")));
db.update("dataitems", cv, "_id=?", new String[]{Integer.toString(status.getInt(status.getColumnIndex("dataitems_id")))});
}
status.close();
}
}

View File

@ -1,70 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.wearable;
import android.content.Context;
import android.content.SharedPreferences;
import java.util.HashMap;
import java.util.Map;
public class RpcHelper {
private final Map<String, RpcConnectionState> rpcStateMap = new HashMap<String, RpcConnectionState>();
private final SharedPreferences preferences;
private final Context context;
public RpcHelper(Context context) {
this.context = context;
this.preferences = context.getSharedPreferences("wearable.rpc_service.settings", 0);
}
private String getRpcConnectionId(String packageName, String targetNodeId, String path) {
String mode = "lo";
if (packageName.equals("com.google.android.wearable.app") && path.startsWith("/s3"))
mode = "hi";
return targetNodeId + ":" + mode;
}
public RpcHelper.RpcConnectionState useConnectionState(String packageName, String targetNodeId, String path) {
String rpcConnectionId = getRpcConnectionId(packageName, targetNodeId, path);
synchronized (rpcStateMap) {
if (!rpcStateMap.containsKey(rpcConnectionId)) {
int g = preferences.getInt(rpcConnectionId, 1)+1;
preferences.edit().putInt(rpcConnectionId, g).apply();
rpcStateMap.put(rpcConnectionId, new RpcConnectionState(g));
}
RpcHelper.RpcConnectionState res = rpcStateMap.get(rpcConnectionId);
res.lastRequestId++;
return res.freeze();
}
}
public static class RpcConnectionState {
public int generation;
public int lastRequestId;
public RpcConnectionState(int generation) {
this.generation = generation;
}
public RpcConnectionState freeze() {
RpcConnectionState res = new RpcConnectionState(generation);
res.lastRequestId = lastRequestId;
return res;
}
}
}

View File

@ -1,634 +0,0 @@
/*
* Copyright (C) 2013-2019 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.wearable;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
import androidx.annotation.Nullable;
import com.google.android.gms.common.data.DataHolder;
import com.google.android.gms.wearable.Asset;
import com.google.android.gms.wearable.ConnectionConfiguration;
import com.google.android.gms.wearable.Node;
import com.google.android.gms.wearable.internal.IWearableListener;
import com.google.android.gms.wearable.internal.MessageEventParcelable;
import com.google.android.gms.wearable.internal.NodeParcelable;
import com.google.android.gms.wearable.internal.PutDataRequest;
import org.microg.gms.common.PackageUtils;
import org.microg.gms.common.RemoteListenerProxy;
import org.microg.gms.common.Utils;
import org.microg.wearable.SocketConnectionThread;
import org.microg.wearable.WearableConnection;
import org.microg.wearable.proto.AckAsset;
import org.microg.wearable.proto.AppKey;
import org.microg.wearable.proto.AppKeys;
import org.microg.wearable.proto.Connect;
import org.microg.wearable.proto.FetchAsset;
import org.microg.wearable.proto.FilePiece;
import org.microg.wearable.proto.Request;
import org.microg.wearable.proto.RootMessage;
import org.microg.wearable.proto.SetAsset;
import org.microg.wearable.proto.SetDataItem;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import okio.ByteString;
public class WearableImpl {
private static final String TAG = "GmsWear";
private static final int WEAR_TCP_PORT = 5601;
private final Context context;
private final NodeDatabaseHelper nodeDatabase;
private final ConfigurationDatabaseHelper configDatabase;
private final Map<String, List<ListenerInfo>> listeners = new HashMap<String, List<ListenerInfo>>();
private final Set<Node> connectedNodes = new HashSet<Node>();
private final Map<String, WearableConnection> activeConnections = new HashMap<String, WearableConnection>();
private RpcHelper rpcHelper;
private SocketConnectionThread sct;
private ConnectionConfiguration[] configurations;
private boolean configurationsUpdated = false;
private ClockworkNodePreferences clockworkNodePreferences;
public Handler networkHandler;
public WearableImpl(Context context, NodeDatabaseHelper nodeDatabase, ConfigurationDatabaseHelper configDatabase) {
this.context = context;
this.nodeDatabase = nodeDatabase;
this.configDatabase = configDatabase;
this.clockworkNodePreferences = new ClockworkNodePreferences(context);
this.rpcHelper = new RpcHelper(context);
new Thread(() -> {
Looper.prepare();
networkHandler = new Handler(Looper.myLooper());
Looper.loop();
}).start();
}
public String getLocalNodeId() {
return clockworkNodePreferences.getLocalNodeId();
}
public DataItemRecord putDataItem(String packageName, String signatureDigest, String source, DataItemInternal dataItem) {
DataItemRecord record = new DataItemRecord();
record.packageName = packageName;
record.signatureDigest = signatureDigest;
record.deleted = false;
record.source = source;
record.dataItem = dataItem;
record.v1SeqId = clockworkNodePreferences.getNextSeqId();
if (record.source.equals(getLocalNodeId())) record.seqId = record.v1SeqId;
nodeDatabase.putRecord(record);
return record;
}
public DataItemRecord putDataItem(DataItemRecord record) {
nodeDatabase.putRecord(record);
if (!record.assetsAreReady) {
for (Asset asset : record.dataItem.getAssets().values()) {
if (!nodeDatabase.hasAsset(asset)) {
Log.d(TAG, "Asset is missing: " + asset);
}
}
}
Intent intent = new Intent("com.google.android.gms.wearable.DATA_CHANGED");
intent.setPackage(record.packageName);
intent.setData(record.dataItem.uri);
invokeListeners(intent, listener -> listener.onDataChanged(record.toEventDataHolder()));
return record;
}
private Asset prepareAsset(String packageName, Asset asset) {
if (asset.getFd() != null && asset.data == null) {
try {
asset.data = Utils.readStreamToEnd(new FileInputStream(asset.getFd().getFileDescriptor()));
} catch (IOException e) {
Log.w(TAG, e);
}
}
if (asset.data != null) {
String digest = calculateDigest(asset.data);
File assetFile = createAssetFile(digest);
boolean success = assetFile.exists();
if (!success) {
File tmpFile = new File(assetFile.getParent(), assetFile.getName() + ".tmp");
try {
FileOutputStream stream = new FileOutputStream(tmpFile);
stream.write(asset.data);
stream.close();
success = tmpFile.renameTo(assetFile);
} catch (IOException e) {
Log.w(TAG, e);
}
}
if (success) {
Log.d(TAG, "Successfully created asset file " + assetFile);
return Asset.createFromRef(digest);
} else {
Log.w(TAG, "Failed creating asset file " + assetFile);
}
}
return null;
}
public File createAssetFile(String digest) {
File dir = new File(new File(context.getFilesDir(), "assets"), digest.substring(digest.length() - 2));
dir.mkdirs();
return new File(dir, digest + ".asset");
}
private File createAssetReceiveTempFile(String name) {
File dir = new File(context.getFilesDir(), "piece");
dir.mkdirs();
return new File(dir, name);
}
private String calculateDigest(byte[] data) {
try {
return Base64.encodeToString(MessageDigest.getInstance("SHA1").digest(data), Base64.NO_WRAP | Base64.NO_PADDING | Base64.URL_SAFE);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
public synchronized ConnectionConfiguration[] getConfigurations() {
if (configurations == null) {
configurations = configDatabase.getAllConfigurations();
}
if (configurationsUpdated) {
configurationsUpdated = false;
ConnectionConfiguration[] newConfigurations = configDatabase.getAllConfigurations();
for (ConnectionConfiguration configuration : configurations) {
for (ConnectionConfiguration newConfiguration : newConfigurations) {
if (newConfiguration.name.equals(configuration.name)) {
newConfiguration.connected = configuration.connected;
newConfiguration.peerNodeId = configuration.peerNodeId;
newConfiguration.nodeId = configuration.nodeId;
break;
}
}
}
configurations = newConfigurations;
}
Log.d(TAG, "Configurations reported: " + Arrays.toString(configurations));
return configurations;
}
private void addConnectedNode(Node node) {
connectedNodes.add(node);
onConnectedNodes(getConnectedNodesParcelableList());
}
private void removeConnectedNode(String nodeId) {
for (Node connectedNode : new ArrayList<Node>(connectedNodes)) {
if (connectedNode.getId().equals(nodeId))
connectedNodes.remove(connectedNode);
}
onConnectedNodes(getConnectedNodesParcelableList());
}
public Context getContext() {
return context;
}
public void syncToPeer(String peerNodeId, String nodeId, long seqId) {
Log.d(TAG, "-- Start syncing over to " + peerNodeId + ", nodeId " + nodeId + " starting with seqId " + seqId);
Cursor cursor = nodeDatabase.getModifiedDataItems(nodeId, seqId, true);
if (cursor != null) {
while (cursor.moveToNext()) {
if (!syncRecordToPeer(peerNodeId, DataItemRecord.fromCursor(cursor))) break;
}
cursor.close();
}
Log.d(TAG, "-- Done syncing over to " + peerNodeId + ", nodeId " + nodeId + " starting with seqId " + seqId);
}
void syncRecordToAll(DataItemRecord record) {
for (String nodeId : new ArrayList<String>(activeConnections.keySet())) {
syncRecordToPeer(nodeId, record);
}
}
private boolean syncRecordToPeer(String nodeId, DataItemRecord record) {
for (Asset asset : record.dataItem.getAssets().values()) {
try {
syncAssetToPeer(nodeId, record, asset);
} catch (Exception e) {
Log.w(TAG, "Could not sync asset " + asset + " for " + nodeId + " and " + record, e);
closeConnection(nodeId);
return false;
}
}
try {
SetDataItem item = record.toSetDataItem();
activeConnections.get(nodeId).writeMessage(new RootMessage.Builder().setDataItem(item).build());
} catch (Exception e) {
Log.w(TAG, e);
closeConnection(nodeId);
return false;
}
return true;
}
private void syncAssetToPeer(String nodeId, DataItemRecord record, Asset asset) throws IOException {
RootMessage announceMessage = new RootMessage.Builder().setAsset(new SetAsset.Builder()
.digest(asset.getDigest())
.appkeys(new AppKeys(Collections.singletonList(new AppKey(record.packageName, record.signatureDigest))))
.build()).hasAsset(true).build();
activeConnections.get(nodeId).writeMessage(announceMessage);
File assetFile = createAssetFile(asset.getDigest());
String fileName = calculateDigest(announceMessage.toByteArray());
FileInputStream fis = new FileInputStream(assetFile);
byte[] arr = new byte[12215];
ByteString lastPiece = null;
int c = 0;
while ((c = fis.read(arr)) > 0) {
if (lastPiece != null) {
activeConnections.get(nodeId).writeMessage(new RootMessage.Builder().filePiece(new FilePiece(fileName, false, lastPiece, null)).build());
}
lastPiece = ByteString.of(arr, 0, c);
}
activeConnections.get(nodeId).writeMessage(new RootMessage.Builder().filePiece(new FilePiece(fileName, true, lastPiece, asset.getDigest())).build());
}
public void addAssetToDatabase(Asset asset, List<AppKey> appKeys) {
nodeDatabase.putAsset(asset, false);
for (AppKey appKey : appKeys) {
nodeDatabase.allowAssetAccess(asset.getDigest(), appKey.packageName, appKey.signatureDigest);
}
}
public long getCurrentSeqId(String nodeId) {
return nodeDatabase.getCurrentSeqId(nodeId);
}
public void handleFilePiece(WearableConnection connection, String fileName, byte[] bytes, String finalPieceDigest) {
File file = createAssetReceiveTempFile(fileName);
try {
FileOutputStream fos = new FileOutputStream(file, true);
fos.write(bytes);
fos.close();
} catch (IOException e) {
Log.w(TAG, e);
}
if (finalPieceDigest != null) {
// This is a final piece. If digest matches we're so happy!
try {
String digest = calculateDigest(Utils.readStreamToEnd(new FileInputStream(file)));
if (digest.equals(finalPieceDigest)) {
if (file.renameTo(createAssetFile(digest))) {
nodeDatabase.markAssetAsPresent(digest);
connection.writeMessage(new RootMessage.Builder().ackAsset(new AckAsset(digest)).build());
} else {
Log.w(TAG, "Could not rename to target file name. delete=" + file.delete());
}
} else {
Log.w(TAG, "Received digest does not match. delete=" + file.delete());
}
} catch (IOException e) {
Log.w(TAG, "Failed working with temp file. delete=" + file.delete(), e);
}
}
}
public void onConnectReceived(WearableConnection connection, String nodeId, Connect connect) {
for (ConnectionConfiguration config : getConfigurations()) {
if (config.nodeId.equals(nodeId)) {
if (config.nodeId != nodeId) {
config.nodeId = connect.id;
configDatabase.putConfiguration(config, nodeId);
}
config.peerNodeId = connect.id;
config.connected = true;
}
}
Log.d(TAG, "Adding connection to list of open connections: " + connection + " with connect " + connect);
activeConnections.put(connect.id, connection);
onPeerConnected(new NodeParcelable(connect.id, connect.name));
// Fetch missing assets
Cursor cursor = nodeDatabase.listMissingAssets();
if (cursor != null) {
while (cursor.moveToNext()) {
try {
Log.d(TAG, "Fetch for " + cursor.getString(12));
connection.writeMessage(new RootMessage.Builder()
.fetchAsset(new FetchAsset.Builder()
.assetName(cursor.getString(12))
.packageName(cursor.getString(1))
.signatureDigest(cursor.getString(2))
.permission(false)
.build()).build());
} catch (IOException e) {
Log.w(TAG, e);
closeConnection(connect.id);
}
}
cursor.close();
}
}
public void onDisconnectReceived(WearableConnection connection, Connect connect) {
for (ConnectionConfiguration config : getConfigurations()) {
if (config.nodeId.equals(connect.id)) {
config.connected = false;
}
}
Log.d(TAG, "Removing connection from list of open connections: " + connection);
activeConnections.remove(connect.id);
onPeerDisconnected(new NodeParcelable(connect.id, connect.name));
}
public List<NodeParcelable> getConnectedNodesParcelableList() {
List<NodeParcelable> nodes = new ArrayList<NodeParcelable>();
for (Node connectedNode : connectedNodes) {
nodes.add(new NodeParcelable(connectedNode));
}
return nodes;
}
interface ListenerInvoker {
void invoke(IWearableListener listener) throws RemoteException;
}
private void invokeListeners(@Nullable Intent intent, ListenerInvoker invoker) {
for (String packageName : new ArrayList<>(listeners.keySet())) {
List<ListenerInfo> listeners = this.listeners.get(packageName);
if (listeners == null) continue;
for (int i = 0; i < listeners.size(); i++) {
boolean filterMatched = false;
if (intent != null) {
for (IntentFilter filter : listeners.get(i).filters) {
filterMatched |= filter.match(context.getContentResolver(), intent, false, TAG) > 0;
}
}
if (filterMatched || listeners.get(i).filters.length == 0) {
try {
invoker.invoke(listeners.get(i).listener);
} catch (RemoteException e) {
Log.w(TAG, "Registered listener at package " + packageName + " failed, removing.");
listeners.remove(i);
i--;
}
}
}
if (listeners.isEmpty()) {
this.listeners.remove(packageName);
}
}
if (intent != null) {
try {
invoker.invoke(RemoteListenerProxy.get(context, intent, IWearableListener.class, "com.google.android.gms.wearable.BIND_LISTENER"));
} catch (RemoteException e) {
Log.w(TAG, "Failed to deliver message received to " + intent, e);
}
}
}
public void onPeerConnected(NodeParcelable node) {
Log.d(TAG, "onPeerConnected: " + node);
invokeListeners(null, listener -> listener.onPeerConnected(node));
addConnectedNode(node);
}
public void onPeerDisconnected(NodeParcelable node) {
Log.d(TAG, "onPeerDisconnected: " + node);
invokeListeners(null, listener -> listener.onPeerDisconnected(node));
removeConnectedNode(node.getId());
}
public void onConnectedNodes(List<NodeParcelable> nodes) {
Log.d(TAG, "onConnectedNodes: " + nodes);
invokeListeners(null, listener -> listener.onConnectedNodes(nodes));
}
public DataItemRecord putData(PutDataRequest request, String packageName) {
DataItemInternal dataItem = new DataItemInternal(fixHost(request.getUri().getHost(), true), request.getUri().getPath());
for (Map.Entry<String, Asset> assetEntry : request.getAssets().entrySet()) {
Asset asset = prepareAsset(packageName, assetEntry.getValue());
if (asset != null) {
nodeDatabase.putAsset(asset, true);
dataItem.addAsset(assetEntry.getKey(), asset);
}
}
dataItem.data = request.getData();
DataItemRecord record = putDataItem(packageName, PackageUtils.firstSignatureDigest(context, packageName), getLocalNodeId(), dataItem);
syncRecordToAll(record);
return record;
}
public DataHolder getDataItemsAsHolder(String packageName) {
Cursor dataHolderItems = nodeDatabase.getDataItemsForDataHolder(packageName, PackageUtils.firstSignatureDigest(context, packageName));
return new DataHolder(dataHolderItems, 0, null);
}
private String fixHost(String host, boolean nothingToLocal) {
if (TextUtils.isEmpty(host) && nothingToLocal) return getLocalNodeId();
if (TextUtils.isEmpty(host)) return null;
if (host.equals("local")) return getLocalNodeId();
return host;
}
public DataHolder getDataItemsByUriAsHolder(Uri uri, String packageName) {
String firstSignature;
try {
firstSignature = PackageUtils.firstSignatureDigest(context, packageName);
} catch (Exception e) {
return null;
}
Cursor dataHolderItems = nodeDatabase.getDataItemsForDataHolderByHostAndPath(packageName, firstSignature, fixHost(uri.getHost(), false), uri.getPath());
DataHolder dataHolder = new DataHolder(dataHolderItems, 0, null);
Log.d(TAG, "Returning data holder of size " + dataHolder.getCount() + " for query " + uri);
return dataHolder;
}
public synchronized void addListener(String packageName, IWearableListener listener, IntentFilter[] filters) {
if (!listeners.containsKey(packageName)) {
listeners.put(packageName, new ArrayList<ListenerInfo>());
}
listeners.get(packageName).add(new ListenerInfo(listener, filters));
}
public void removeListener(IWearableListener listener) {
for (List<ListenerInfo> list : listeners.values()) {
for (int i = 0; i < list.size(); i++) {
if (list.get(i).listener.equals(listener)) {
list.remove(i);
i--;
}
}
}
}
public void enableConnection(String name) {
configDatabase.setEnabledState(name, true);
configurationsUpdated = true;
if (name.equals("server") && sct == null) {
Log.d(TAG, "Starting server on :" + WEAR_TCP_PORT);
(sct = SocketConnectionThread.serverListen(WEAR_TCP_PORT, new MessageHandler(this, configDatabase.getConfiguration(name)))).start();
}
}
public void disableConnection(String name) {
configDatabase.setEnabledState(name, false);
configurationsUpdated = true;
if (name.equals("server") && sct != null) {
activeConnections.remove(sct.getWearableConnection());
sct.close();
sct.interrupt();
sct = null;
}
}
public void deleteConnection(String name) {
configDatabase.deleteConfiguration(name);
configurationsUpdated = true;
}
public void createConnection(ConnectionConfiguration config) {
if (config.nodeId == null) config.nodeId = getLocalNodeId();
Log.d(TAG, "putConfig[nyp]: " + config);
configDatabase.putConfiguration(config);
configurationsUpdated = true;
}
public int deleteDataItems(Uri uri, String packageName) {
List<DataItemRecord> records = nodeDatabase.deleteDataItems(packageName, PackageUtils.firstSignatureDigest(context, packageName), fixHost(uri.getHost(), false), uri.getPath());
for (DataItemRecord record : records) {
syncRecordToAll(record);
}
return records.size();
}
public void sendMessageReceived(String packageName, MessageEventParcelable messageEvent) {
Log.d(TAG, "onMessageReceived: " + messageEvent);
Intent intent = new Intent("com.google.android.gms.wearable.MESSAGE_RECEIVED");
intent.setPackage(packageName);
intent.setData(Uri.parse("wear://" + getLocalNodeId() + "/" + messageEvent.getPath()));
invokeListeners(intent, listener -> listener.onMessageReceived(messageEvent));
}
public DataItemRecord getDataItemByUri(Uri uri, String packageName) {
Cursor cursor = nodeDatabase.getDataItemsByHostAndPath(packageName, PackageUtils.firstSignatureDigest(context, packageName), fixHost(uri.getHost(), true), uri.getPath());
DataItemRecord record = null;
if (cursor != null) {
if (cursor.moveToNext()) {
record = DataItemRecord.fromCursor(cursor);
}
cursor.close();
}
Log.d(TAG, "getDataItem: " + record);
return record;
}
private IWearableListener getListener(String packageName, String action, Uri uri) {
Intent intent = new Intent(action);
intent.setPackage(packageName);
intent.setData(uri);
return RemoteListenerProxy.get(context, intent, IWearableListener.class, "com.google.android.gms.wearable.BIND_LISTENER");
}
private void closeConnection(String nodeId) {
WearableConnection connection = activeConnections.get(nodeId);
try {
connection.close();
} catch (IOException e1) {
Log.w(TAG, e1);
}
if (connection == sct.getWearableConnection()) {
sct.close();
sct = null;
}
activeConnections.remove(nodeId);
for (ConnectionConfiguration config : getConfigurations()) {
if (nodeId.equals(config.nodeId) || nodeId.equals(config.peerNodeId)) {
config.connected = false;
}
}
onPeerDisconnected(new NodeParcelable(nodeId, "Wear device"));
Log.d(TAG, "Closed connection to " + nodeId + " on error");
}
public int sendMessage(String packageName, String targetNodeId, String path, byte[] data) {
if (activeConnections.containsKey(targetNodeId)) {
WearableConnection connection = activeConnections.get(targetNodeId);
RpcHelper.RpcConnectionState state = rpcHelper.useConnectionState(packageName, targetNodeId, path);
try {
connection.writeMessage(new RootMessage.Builder().rpcRequest(new Request.Builder()
.targetNodeId(targetNodeId)
.path(path)
.rawData(ByteString.of(data))
.packageName(packageName)
.signatureDigest(PackageUtils.firstSignatureDigest(context, packageName))
.sourceNodeId(getLocalNodeId())
.generation(state.generation)
.requestId(state.lastRequestId)
.build()).build());
} catch (IOException e) {
Log.w(TAG, "Error while writing, closing link", e);
closeConnection(targetNodeId);
return -1;
}
return (state.generation + 527) * 31 + state.lastRequestId;
}
Log.d(TAG, targetNodeId + " seems not reachable");
return -1;
}
public void stop() {
this.networkHandler.getLooper().quit();
}
private class ListenerInfo {
private IWearableListener listener;
private IntentFilter[] filters;
private ListenerInfo(IWearableListener listener, IntentFilter[] filters) {
this.listener = listener;
this.filters = filters;
}
}
}

View File

@ -1,55 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.wearable;
import android.os.RemoteException;
import com.google.android.gms.common.internal.GetServiceRequest;
import com.google.android.gms.common.internal.IGmsCallbacks;
import org.microg.gms.BaseService;
import org.microg.gms.common.GmsService;
import org.microg.gms.common.PackageUtils;
public class WearableService extends BaseService {
private WearableImpl wearable;
public WearableService() {
super("GmsWearSvc", GmsService.WEARABLE);
}
@Override
public void onCreate() {
super.onCreate();
ConfigurationDatabaseHelper configurationDatabaseHelper = new ConfigurationDatabaseHelper(getApplicationContext());
NodeDatabaseHelper nodeDatabaseHelper = new NodeDatabaseHelper(getApplicationContext());
wearable = new WearableImpl(getApplicationContext(), nodeDatabaseHelper, configurationDatabaseHelper);
}
@Override
public void onDestroy() {
super.onDestroy();
wearable.stop();
}
@Override
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException {
PackageUtils.getAndCheckCallingPackage(this, request.packageName);
callback.onPostInitComplete(0, new WearableServiceImpl(this, wearable, request.packageName), null);
}
}

View File

@ -1,527 +0,0 @@
/*
* Copyright (C) 2013-2019 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.wearable;
import android.content.Context;
import android.net.Uri;
import android.os.Handler;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.util.Base64;
import android.util.Log;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.wearable.Asset;
import com.google.android.gms.wearable.ConnectionConfiguration;
import com.google.android.gms.wearable.internal.AddListenerRequest;
import com.google.android.gms.wearable.internal.AddLocalCapabilityResponse;
import com.google.android.gms.wearable.internal.AncsNotificationParcelable;
import com.google.android.gms.wearable.internal.CapabilityInfoParcelable;
import com.google.android.gms.wearable.internal.DeleteDataItemsResponse;
import com.google.android.gms.wearable.internal.GetCapabilityResponse;
import com.google.android.gms.wearable.internal.GetCloudSyncSettingResponse;
import com.google.android.gms.wearable.internal.GetConfigResponse;
import com.google.android.gms.wearable.internal.GetConfigsResponse;
import com.google.android.gms.wearable.internal.GetConnectedNodesResponse;
import com.google.android.gms.wearable.internal.GetDataItemResponse;
import com.google.android.gms.wearable.internal.GetFdForAssetResponse;
import com.google.android.gms.wearable.internal.GetLocalNodeResponse;
import com.google.android.gms.wearable.internal.IChannelStreamCallbacks;
import com.google.android.gms.wearable.internal.IWearableCallbacks;
import com.google.android.gms.wearable.internal.IWearableService;
import com.google.android.gms.wearable.internal.NodeParcelable;
import com.google.android.gms.wearable.internal.PutDataRequest;
import com.google.android.gms.wearable.internal.PutDataResponse;
import com.google.android.gms.wearable.internal.RemoveListenerRequest;
import com.google.android.gms.wearable.internal.RemoveLocalCapabilityResponse;
import com.google.android.gms.wearable.internal.SendMessageResponse;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class WearableServiceImpl extends IWearableService.Stub {
private static final String TAG = "GmsWearSvcImpl";
private final Context context;
private final String packageName;
private final WearableImpl wearable;
private final Handler mainHandler;
private final CapabilityManager capabilities;
public WearableServiceImpl(Context context, WearableImpl wearable, String packageName) {
this.context = context;
this.wearable = wearable;
this.packageName = packageName;
this.capabilities = new CapabilityManager(context, wearable, packageName);
this.mainHandler = new Handler(context.getMainLooper());
}
private void postMain(IWearableCallbacks callbacks, RemoteExceptionRunnable runnable) {
mainHandler.post(new CallbackRunnable(callbacks) {
@Override
public void run(IWearableCallbacks callbacks) throws RemoteException {
runnable.run();
}
});
}
private void postNetwork(IWearableCallbacks callbacks, RemoteExceptionRunnable runnable) {
this.wearable.networkHandler.post(new CallbackRunnable(callbacks) {
@Override
public void run(IWearableCallbacks callbacks) throws RemoteException {
runnable.run();
}
});
}
/*
* Config
*/
@Override
public void putConfig(IWearableCallbacks callbacks, final ConnectionConfiguration config) throws RemoteException {
postMain(callbacks, () -> {
wearable.createConnection(config);
callbacks.onStatus(Status.SUCCESS);
});
}
@Override
public void deleteConfig(IWearableCallbacks callbacks, final String name) throws RemoteException {
postMain(callbacks, () -> {
wearable.deleteConnection(name);
callbacks.onStatus(Status.SUCCESS);
});
}
@Override
public void getConfigs(IWearableCallbacks callbacks) throws RemoteException {
Log.d(TAG, "getConfigs");
postMain(callbacks, () -> {
try {
callbacks.onGetConfigsResponse(new GetConfigsResponse(0, wearable.getConfigurations()));
} catch (Exception e) {
callbacks.onGetConfigsResponse(new GetConfigsResponse(8, new ConnectionConfiguration[0]));
}
});
}
@Override
public void enableConfig(IWearableCallbacks callbacks, final String name) throws RemoteException {
Log.d(TAG, "enableConfig: " + name);
postMain(callbacks, () -> {
wearable.enableConnection(name);
callbacks.onStatus(Status.SUCCESS);
});
}
@Override
public void disableConfig(IWearableCallbacks callbacks, final String name) throws RemoteException {
Log.d(TAG, "disableConfig: " + name);
postMain(callbacks, () -> {
wearable.disableConnection(name);
callbacks.onStatus(Status.SUCCESS);
});
}
/*
* DataItems
*/
@Override
public void putData(IWearableCallbacks callbacks, final PutDataRequest request) throws RemoteException {
Log.d(TAG, "putData: " + request.toString(true));
this.wearable.networkHandler.post(new CallbackRunnable(callbacks) {
@Override
public void run(IWearableCallbacks callbacks) throws RemoteException {
DataItemRecord record = wearable.putData(request, packageName);
callbacks.onPutDataResponse(new PutDataResponse(0, record.toParcelable()));
}
});
}
@Override
public void getDataItem(IWearableCallbacks callbacks, final Uri uri) throws RemoteException {
Log.d(TAG, "getDataItem: " + uri);
postMain(callbacks, () -> {
DataItemRecord record = wearable.getDataItemByUri(uri, packageName);
if (record != null) {
callbacks.onGetDataItemResponse(new GetDataItemResponse(0, record.toParcelable()));
} else {
callbacks.onGetDataItemResponse(new GetDataItemResponse(0, null));
}
});
}
@Override
public void getDataItems(final IWearableCallbacks callbacks) throws RemoteException {
Log.d(TAG, "getDataItems: " + callbacks);
postMain(callbacks, () -> {
callbacks.onDataItemChanged(wearable.getDataItemsAsHolder(packageName));
});
}
@Override
public void getDataItemsByUri(IWearableCallbacks callbacks, Uri uri) throws RemoteException {
getDataItemsByUriWithFilter(callbacks, uri, 0);
}
@Override
public void getDataItemsByUriWithFilter(IWearableCallbacks callbacks, final Uri uri, int typeFilter) throws RemoteException {
Log.d(TAG, "getDataItemsByUri: " + uri);
postMain(callbacks, () -> {
callbacks.onDataItemChanged(wearable.getDataItemsByUriAsHolder(uri, packageName));
});
}
@Override
public void deleteDataItems(IWearableCallbacks callbacks, Uri uri) throws RemoteException {
deleteDataItemsWithFilter(callbacks, uri, 0);
}
@Override
public void deleteDataItemsWithFilter(IWearableCallbacks callbacks, final Uri uri, int typeFilter) throws RemoteException {
Log.d(TAG, "deleteDataItems: " + uri);
this.wearable.networkHandler.post(new CallbackRunnable(callbacks) {
@Override
public void run(IWearableCallbacks callbacks) throws RemoteException {
callbacks.onDeleteDataItemsResponse(new DeleteDataItemsResponse(0, wearable.deleteDataItems(uri, packageName)));
}
});
}
@Override
public void sendMessage(IWearableCallbacks callbacks, final String targetNodeId, final String path, final byte[] data) throws RemoteException {
Log.d(TAG, "sendMessage: " + targetNodeId + " / " + path + ": " + (data == null ? null : Base64.encodeToString(data, Base64.NO_WRAP)));
this.wearable.networkHandler.post(new CallbackRunnable(callbacks) {
@Override
public void run(IWearableCallbacks callbacks) throws RemoteException {
SendMessageResponse sendMessageResponse = new SendMessageResponse();
try {
sendMessageResponse.requestId = wearable.sendMessage(packageName, targetNodeId, path, data);
if (sendMessageResponse.requestId == -1) {
sendMessageResponse.statusCode = 4000;
}
} catch (Exception e) {
sendMessageResponse.statusCode = 8;
}
mainHandler.post(() -> {
try {
callbacks.onSendMessageResponse(sendMessageResponse);
} catch (RemoteException e) {
e.printStackTrace();
}
});
}
});
}
@Override
public void getFdForAsset(IWearableCallbacks callbacks, final Asset asset) throws RemoteException {
Log.d(TAG, "getFdForAsset " + asset);
postMain(callbacks, () -> {
// TODO: Access control
try {
callbacks.onGetFdForAssetResponse(new GetFdForAssetResponse(0, ParcelFileDescriptor.open(wearable.createAssetFile(asset.getDigest()), ParcelFileDescriptor.MODE_READ_ONLY)));
} catch (FileNotFoundException e) {
callbacks.onGetFdForAssetResponse(new GetFdForAssetResponse(8, null));
}
});
}
@Override
public void optInCloudSync(IWearableCallbacks callbacks, boolean enable) throws RemoteException {
callbacks.onStatus(Status.SUCCESS);
}
@Override
@Deprecated
public void getCloudSyncOptInDone(IWearableCallbacks callbacks) throws RemoteException {
Log.d(TAG, "unimplemented Method: getCloudSyncOptInDone");
}
@Override
public void setCloudSyncSetting(IWearableCallbacks callbacks, boolean enable) throws RemoteException {
Log.d(TAG, "unimplemented Method: setCloudSyncSetting");
}
@Override
public void getCloudSyncSetting(IWearableCallbacks callbacks) throws RemoteException {
callbacks.onGetCloudSyncSettingResponse(new GetCloudSyncSettingResponse(0, false));
}
@Override
public void getCloudSyncOptInStatus(IWearableCallbacks callbacks) throws RemoteException {
Log.d(TAG, "unimplemented Method: getCloudSyncOptInStatus");
}
@Override
public void sendRemoteCommand(IWearableCallbacks callbacks, byte b) throws RemoteException {
Log.d(TAG, "unimplemented Method: sendRemoteCommand: " + b);
}
@Override
public void getLocalNode(IWearableCallbacks callbacks) throws RemoteException {
postMain(callbacks, () -> {
try {
callbacks.onGetLocalNodeResponse(new GetLocalNodeResponse(0, new NodeParcelable(wearable.getLocalNodeId(), wearable.getLocalNodeId())));
} catch (Exception e) {
callbacks.onGetLocalNodeResponse(new GetLocalNodeResponse(8, null));
}
});
}
@Override
public void getConnectedNodes(IWearableCallbacks callbacks) throws RemoteException {
postMain(callbacks, () -> {
callbacks.onGetConnectedNodesResponse(new GetConnectedNodesResponse(0, wearable.getConnectedNodesParcelableList()));
});
}
/*
* Capability
*/
@Override
public void getConnectedCapability(IWearableCallbacks callbacks, String capability, int nodeFilter) throws RemoteException {
Log.d(TAG, "unimplemented Method: getConnectedCapability " + capability + ", " + nodeFilter);
postMain(callbacks, () -> {
List<NodeParcelable> nodes = new ArrayList<>();
for (String host : capabilities.getNodesForCapability(capability)) {
nodes.add(new NodeParcelable(host, host));
}
CapabilityInfoParcelable capabilityInfo = new CapabilityInfoParcelable(capability, nodes);
callbacks.onGetCapabilityResponse(new GetCapabilityResponse(0, capabilityInfo));
});
}
@Override
public void getConnectedCapaibilties(IWearableCallbacks callbacks, int nodeFilter) throws RemoteException {
Log.d(TAG, "unimplemented Method: getConnectedCapaibilties: " + nodeFilter);
}
@Override
public void addLocalCapability(IWearableCallbacks callbacks, String capability) throws RemoteException {
Log.d(TAG, "unimplemented Method: addLocalCapability: " + capability);
this.wearable.networkHandler.post(new CallbackRunnable(callbacks) {
@Override
public void run(IWearableCallbacks callbacks) throws RemoteException {
callbacks.onAddLocalCapabilityResponse(new AddLocalCapabilityResponse(capabilities.add(capability)));
}
});
}
@Override
public void removeLocalCapability(IWearableCallbacks callbacks, String capability) throws RemoteException {
Log.d(TAG, "unimplemented Method: removeLocalCapability: " + capability);
this.wearable.networkHandler.post(new CallbackRunnable(callbacks) {
@Override
public void run(IWearableCallbacks callbacks) throws RemoteException {
callbacks.onRemoveLocalCapabilityResponse(new RemoveLocalCapabilityResponse(capabilities.remove(capability)));
}
});
}
@Override
public void addListener(IWearableCallbacks callbacks, AddListenerRequest request) throws RemoteException {
if (request.listener != null) {
wearable.addListener(packageName, request.listener, request.intentFilters);
}
callbacks.onStatus(Status.SUCCESS);
}
@Override
public void removeListener(IWearableCallbacks callbacks, RemoveListenerRequest request) throws RemoteException {
wearable.removeListener(request.listener);
callbacks.onStatus(Status.SUCCESS);
}
@Override
public void getStorageInformation(IWearableCallbacks callbacks) throws RemoteException {
Log.d(TAG, "unimplemented Method: getStorageInformation");
}
@Override
public void clearStorage(IWearableCallbacks callbacks) throws RemoteException {
Log.d(TAG, "unimplemented Method: clearStorage");
}
@Override
public void endCall(IWearableCallbacks callbacks) throws RemoteException {
Log.d(TAG, "unimplemented Method: endCall");
}
@Override
public void acceptRingingCall(IWearableCallbacks callbacks) throws RemoteException {
Log.d(TAG, "unimplemented Method: acceptRingingCall");
}
@Override
public void silenceRinger(IWearableCallbacks callbacks) throws RemoteException {
Log.d(TAG, "unimplemented Method: silenceRinger");
}
/*
* Apple Notification Center Service
*/
@Override
public void injectAncsNotificationForTesting(IWearableCallbacks callbacks, AncsNotificationParcelable notification) throws RemoteException {
Log.d(TAG, "unimplemented Method: injectAncsNotificationForTesting: " + notification);
}
@Override
public void doAncsPositiveAction(IWearableCallbacks callbacks, int i) throws RemoteException {
Log.d(TAG, "unimplemented Method: doAncsPositiveAction: " + i);
}
@Override
public void doAncsNegativeAction(IWearableCallbacks callbacks, int i) throws RemoteException {
Log.d(TAG, "unimplemented Method: doAncsNegativeAction: " + i);
}
@Override
public void openChannel(IWearableCallbacks callbacks, String s1, String s2) throws RemoteException {
Log.d(TAG, "unimplemented Method: openChannel; " + s1 + ", " + s2);
}
/*
* Channels
*/
@Override
public void closeChannel(IWearableCallbacks callbacks, String s) throws RemoteException {
Log.d(TAG, "unimplemented Method: closeChannel: " + s);
}
@Override
public void closeChannelWithError(IWearableCallbacks callbacks, String s, int errorCode) throws RemoteException {
Log.d(TAG, "unimplemented Method: closeChannelWithError:" + s + ", " + errorCode);
}
@Override
public void getChannelInputStream(IWearableCallbacks callbacks, IChannelStreamCallbacks channelCallbacks, String s) throws RemoteException {
Log.d(TAG, "unimplemented Method: getChannelInputStream: " + s);
}
@Override
public void getChannelOutputStream(IWearableCallbacks callbacks, IChannelStreamCallbacks channelCallbacks, String s) throws RemoteException {
Log.d(TAG, "unimplemented Method: getChannelOutputStream: " + s);
}
@Override
public void writeChannelInputToFd(IWearableCallbacks callbacks, String s, ParcelFileDescriptor fd) throws RemoteException {
Log.d(TAG, "unimplemented Method: writeChannelInputToFd: " + s);
}
@Override
public void readChannelOutputFromFd(IWearableCallbacks callbacks, String s, ParcelFileDescriptor fd, long l1, long l2) throws RemoteException {
Log.d(TAG, "unimplemented Method: readChannelOutputFromFd: " + s + ", " + l1 + ", " + l2);
}
@Override
public void syncWifiCredentials(IWearableCallbacks callbacks) throws RemoteException {
Log.d(TAG, "unimplemented Method: syncWifiCredentials");
}
/*
* Connection deprecated
*/
@Override
@Deprecated
public void putConnection(IWearableCallbacks callbacks, ConnectionConfiguration config) throws RemoteException {
Log.d(TAG, "unimplemented Method: putConnection");
}
@Override
@Deprecated
public void getConnection(IWearableCallbacks callbacks) throws RemoteException {
Log.d(TAG, "getConfig");
postMain(callbacks, () -> {
ConnectionConfiguration[] configurations = wearable.getConfigurations();
if (configurations == null || configurations.length == 0) {
callbacks.onGetConfigResponse(new GetConfigResponse(1, new ConnectionConfiguration(null, null, 0, 0, false)));
} else {
callbacks.onGetConfigResponse(new GetConfigResponse(0, configurations[0]));
}
});
}
@Override
@Deprecated
public void enableConnection(IWearableCallbacks callbacks) throws RemoteException {
postMain(callbacks, () -> {
ConnectionConfiguration[] configurations = wearable.getConfigurations();
if (configurations.length > 0) {
enableConfig(callbacks, configurations[0].name);
}
});
}
@Override
@Deprecated
public void disableConnection(IWearableCallbacks callbacks) throws RemoteException {
postMain(callbacks, () -> {
ConnectionConfiguration[] configurations = wearable.getConfigurations();
if (configurations.length > 0) {
disableConfig(callbacks, configurations[0].name);
}
});
}
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
if (super.onTransact(code, data, reply, flags)) return true;
Log.d(TAG, "onTransact [unknown]: " + code + ", " + data + ", " + flags);
return false;
}
public abstract class CallbackRunnable implements Runnable {
private IWearableCallbacks callbacks;
public CallbackRunnable(IWearableCallbacks callbacks) {
this.callbacks = callbacks;
}
@Override
public void run() {
try {
run(callbacks);
} catch (RemoteException e) {
mainHandler.post(() -> {
try {
callbacks.onStatus(Status.CANCELED);
} catch (RemoteException e2) {
Log.w(TAG, e);
}
});
}
}
public abstract void run(IWearableCallbacks callbacks) throws RemoteException;
}
public interface RemoteExceptionRunnable {
void run() throws RemoteException;
}
}

View File

@ -1,149 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.wearable.location;
import android.content.Context;
import android.util.Log;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.internal.ClientIdentity;
import com.google.android.gms.location.internal.LocationRequestInternal;
import com.google.android.gms.wearable.DataMap;
import com.google.android.gms.wearable.MessageEvent;
import com.google.android.gms.wearable.Node;
import com.google.android.gms.wearable.Wearable;
import com.google.android.gms.wearable.WearableListenerService;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class WearableLocationService extends WearableListenerService {
private static final String TAG = "GmsWearLocSvc";
public static final String PATH_LOCATION_REQUESTS = "com/google/android/location/fused/wearable/LOCATION_REQUESTS";
public static final String PATH_CAPABILITY_QUERY = "com/google/android/location/fused/wearable/CAPABILITY_QUERY";
public static final String PATH_CAPABILITY = "com/google/android/location/fused/wearable/CAPABILITY";
private GoogleApiClient apiClient;
private Map<String, Collection<LocationRequestInternal>> requestMap = new HashMap<String, Collection<LocationRequestInternal>>();
@Override
public void onMessageReceived(MessageEvent messageEvent) {
if (messageEvent.getPath().equals(PATH_LOCATION_REQUESTS)) {
DataMap dataMap = DataMap.fromByteArray(messageEvent.getData());
onLocationRequests(messageEvent.getSourceNodeId(), readLocationRequestList(dataMap, this), dataMap.getBoolean("TRIGGER_UPDATE", false));
} else if (messageEvent.getPath().equals(PATH_CAPABILITY_QUERY)) {
onCapabilityQuery(messageEvent.getSourceNodeId());
}
}
@Override
public void onPeerDisconnected(Node peer) {
onLocationRequests(peer.getId(), null, false);
}
public void onLocationRequests(String nodeId, Collection<LocationRequestInternal> requests, boolean triggerUpdate) {
if (requests == null || requests.isEmpty()) {
requestMap.remove(nodeId);
} else {
requestMap.put(nodeId, requests);
}
Log.d(TAG, "Requests: "+requestMap.entrySet());
// TODO actually request
}
public void onCapabilityQuery(String nodeId) {
Wearable.MessageApi.sendMessage(getApiClient(), nodeId, PATH_CAPABILITY, writeLocationCapability(new DataMap(), true).toByteArray());
}
private GoogleApiClient getApiClient() {
if (apiClient == null) {
apiClient = new GoogleApiClient.Builder(this).addApi(Wearable.API).build();
}
if (!apiClient.isConnected()) {
apiClient.connect();
}
return apiClient;
}
public static DataMap writeLocationCapability(DataMap dataMap, boolean locationCapable) {
dataMap.putBoolean("CAPABILITY_LOCATION", locationCapable);
return dataMap;
}
public static Collection<LocationRequestInternal> readLocationRequestList(DataMap dataMap, Context context) {
if (!dataMap.containsKey("REQUEST_LIST")) {
Log.w(TAG, "malformed DataMap: missing key REQUEST_LIST");
return Collections.emptyList();
}
List<DataMap> requestMapList = dataMap.getDataMapArrayList("REQUEST_LIST");
List<LocationRequestInternal> locationRequests = new ArrayList<LocationRequestInternal>();
for (DataMap map : requestMapList) {
locationRequests.add(readLocationRequest(map, context));
}
return locationRequests;
}
private static LocationRequestInternal readLocationRequest(DataMap dataMap, Context context) {
LocationRequestInternal request = new LocationRequestInternal();
request.triggerUpdate = true;
request.request = new LocationRequest();
request.clients = Collections.emptyList();
if (dataMap.containsKey("PRIORITY"))
request.request.setPriority(dataMap.getInt("PRIORITY", 0));
if (dataMap.containsKey("INTERVAL_MS"))
request.request.setInterval(dataMap.getLong("INTERVAL_MS", 0));
if (dataMap.containsKey("FASTEST_INTERVAL_MS"))
request.request.setFastestInterval(dataMap.getLong("FASTEST_INTERVAL_MS", 0));
//if (dataMap.containsKey("MAX_WAIT_TIME_MS"))
if (dataMap.containsKey("SMALLEST_DISPLACEMENT_METERS"))
request.request.setSmallestDisplacement(dataMap.getFloat("SMALLEST_DISPLACEMENT_METERS", 0));
if (dataMap.containsKey("NUM_UPDATES"))
request.request.setNumUpdates(dataMap.getInt("NUM_UPDATES", 0));
if (dataMap.containsKey("EXPIRATION_DURATION_MS"))
request.request.setExpirationDuration(dataMap.getLong("EXPIRATION_DURATION_MS", 0));
if (dataMap.containsKey("TAG"))
request.tag = dataMap.getString("TAG");
if (dataMap.containsKey("CLIENTS_PACKAGE_ARRAY")) {
String[] packages = dataMap.getStringArray("CLIENTS_PACKAGE_ARRAY");
if (packages != null) {
request.clients = new ArrayList<ClientIdentity>();
for (String packageName : packages) {
request.clients.add(generateClientIdentity(packageName, context));
}
}
}
return request;
}
private static ClientIdentity generateClientIdentity(String packageName, Context context) {
return null;
/*try {
return new ClientIdentity(context.getPackageManager().getApplicationInfo(packageName, 0).uid, packageName);
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Unknown client identity: " + packageName, e);
return new ClientIdentity(context.getApplicationInfo().uid, context.getPackageName());
}*/
}
}

View File

@ -0,0 +1 @@
[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":19420021,"versionName":"0.2.10.19420-dirty-vtm","enabled":true,"outputFile":"play-services-core-vtm-release.apk","fullName":"vtmRelease","baseName":"vtm-release","dirName":""},"path":"play-services-core-vtm-release.apk","properties":{}}]

View File

@ -44,6 +44,5 @@ android {
dependencies {
api project(':play-services-base')
api project(':play-services-wearable-api')
implementation 'com.squareup.wire:wire-runtime:1.6.1'
}

View File

@ -20,9 +20,7 @@
-keep public class com.google.android.gms.maps.internal.CreatorImpl { public *; }
-keep public class com.google.android.gms.common.security.ProviderInstallerImpl { public *; }
-keep public class com.google.android.gms.plus.plusone.PlusOneButtonCreatorImpl { public *; }
-keep public class com.google.android.gms.dynamic.IObjectWrapper { public *; }
-keep public class com.google.android.gms.chimera.container.DynamiteLoaderImpl { public *; }
-keep public class com.google.android.gms.dynamite.descriptors.** { public *; }
-keep public class com.google.android.gms.cast.framework.internal.CastDynamiteModuleImpl { public *; }
# Keep AutoSafeParcelables
@ -37,7 +35,6 @@
# Keep our stuff
-keep class org.microg.** { *; }
-keep class com.google.android.gms.** { *; }
# Keep asInterface method cause it's accessed from SafeParcel
-keepattributes InnerClasses

View File

@ -1,23 +1,14 @@
include ':wearable-lib'
include ':play-services-basement'
include ':play-services-api'
include ':play-services-cast-api'
include ':play-services-cast-framework-api'
include ':play-services-iid-api'
include ':play-services-location-api'
include ':play-services-wearable-api'
include ':play-services-base'
include ':play-services-tasks'
include ':play-services-wearable'
include ':play-services-base-core'
include ':play-services-location-core'
include ':play-services-maps-core-mapbox'
include ':play-services-maps-core-vtm'
include ':play-services-maps-core-vtm:vtm-microg-theme'
include ':play-services-core'
include ':play-services-core:microg-ui-tools'