Merge "Move "alerted recently" logic out of NEM"
diff --git a/Android.bp b/Android.bp
index f6d49e9..15befae 100644
--- a/Android.bp
+++ b/Android.bp
@@ -739,6 +739,7 @@
"android.hardware.vibrator-V1.0-java",
"android.hardware.vibrator-V1.1-java",
"android.hardware.vibrator-V1.2-java",
+ "android.hardware.vibrator-V1.3-java",
"android.hardware.wifi-V1.0-java-constants",
"android.hardware.radio-V1.0-java",
"android.hardware.radio-V1.3-java",
diff --git a/api/current.txt b/api/current.txt
index d3aa606..7e0c33c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8532,42 +8532,44 @@
field public static final java.lang.String VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY = "android.bluetooth.headset.intent.category.companyid";
}
- public final class BluetoothHealth implements android.bluetooth.BluetoothProfile {
- method public boolean connectChannelToSource(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration);
- method public boolean disconnectChannel(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration, int);
+ public final deprecated class BluetoothHealth implements android.bluetooth.BluetoothProfile {
+ ctor public BluetoothHealth();
+ method public deprecated boolean connectChannelToSource(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration);
+ method public deprecated boolean disconnectChannel(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration, int);
method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
method public int getConnectionState(android.bluetooth.BluetoothDevice);
method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
- method public android.os.ParcelFileDescriptor getMainChannelFd(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration);
- method public boolean registerSinkAppConfiguration(java.lang.String, int, android.bluetooth.BluetoothHealthCallback);
- method public boolean unregisterAppConfiguration(android.bluetooth.BluetoothHealthAppConfiguration);
- field public static final int APP_CONFIG_REGISTRATION_FAILURE = 1; // 0x1
- field public static final int APP_CONFIG_REGISTRATION_SUCCESS = 0; // 0x0
- field public static final int APP_CONFIG_UNREGISTRATION_FAILURE = 3; // 0x3
- field public static final int APP_CONFIG_UNREGISTRATION_SUCCESS = 2; // 0x2
- field public static final int CHANNEL_TYPE_RELIABLE = 10; // 0xa
- field public static final int CHANNEL_TYPE_STREAMING = 11; // 0xb
- field public static final int SINK_ROLE = 2; // 0x2
- field public static final int SOURCE_ROLE = 1; // 0x1
- field public static final int STATE_CHANNEL_CONNECTED = 2; // 0x2
- field public static final int STATE_CHANNEL_CONNECTING = 1; // 0x1
- field public static final int STATE_CHANNEL_DISCONNECTED = 0; // 0x0
- field public static final int STATE_CHANNEL_DISCONNECTING = 3; // 0x3
+ method public deprecated android.os.ParcelFileDescriptor getMainChannelFd(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration);
+ method public deprecated boolean registerSinkAppConfiguration(java.lang.String, int, android.bluetooth.BluetoothHealthCallback);
+ method public deprecated boolean unregisterAppConfiguration(android.bluetooth.BluetoothHealthAppConfiguration);
+ field public static final deprecated int APP_CONFIG_REGISTRATION_FAILURE = 1; // 0x1
+ field public static final deprecated int APP_CONFIG_REGISTRATION_SUCCESS = 0; // 0x0
+ field public static final deprecated int APP_CONFIG_UNREGISTRATION_FAILURE = 3; // 0x3
+ field public static final deprecated int APP_CONFIG_UNREGISTRATION_SUCCESS = 2; // 0x2
+ field public static final deprecated int CHANNEL_TYPE_RELIABLE = 10; // 0xa
+ field public static final deprecated int CHANNEL_TYPE_STREAMING = 11; // 0xb
+ field public static final deprecated int SINK_ROLE = 2; // 0x2
+ field public static final deprecated int SOURCE_ROLE = 1; // 0x1
+ field public static final deprecated int STATE_CHANNEL_CONNECTED = 2; // 0x2
+ field public static final deprecated int STATE_CHANNEL_CONNECTING = 1; // 0x1
+ field public static final deprecated int STATE_CHANNEL_DISCONNECTED = 0; // 0x0
+ field public static final deprecated int STATE_CHANNEL_DISCONNECTING = 3; // 0x3
}
- public final class BluetoothHealthAppConfiguration implements android.os.Parcelable {
+ public final deprecated class BluetoothHealthAppConfiguration implements android.os.Parcelable {
+ ctor public BluetoothHealthAppConfiguration();
method public int describeContents();
- method public int getDataType();
- method public java.lang.String getName();
- method public int getRole();
+ method public deprecated int getDataType();
+ method public deprecated java.lang.String getName();
+ method public deprecated int getRole();
method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothHealthAppConfiguration> CREATOR;
+ field public static final deprecated android.os.Parcelable.Creator<android.bluetooth.BluetoothHealthAppConfiguration> CREATOR;
}
- public abstract class BluetoothHealthCallback {
+ public abstract deprecated class BluetoothHealthCallback {
ctor public BluetoothHealthCallback();
- method public void onHealthAppConfigurationStatusChange(android.bluetooth.BluetoothHealthAppConfiguration, int);
- method public void onHealthChannelStateChange(android.bluetooth.BluetoothHealthAppConfiguration, android.bluetooth.BluetoothDevice, int, int, android.os.ParcelFileDescriptor, int);
+ method public deprecated void onHealthAppConfigurationStatusChange(android.bluetooth.BluetoothHealthAppConfiguration, int);
+ method public deprecated void onHealthChannelStateChange(android.bluetooth.BluetoothHealthAppConfiguration, android.bluetooth.BluetoothDevice, int, int, android.os.ParcelFileDescriptor, int);
}
public final class BluetoothHidDevice implements android.bluetooth.BluetoothProfile {
@@ -8664,7 +8666,7 @@
field public static final int GATT = 7; // 0x7
field public static final int GATT_SERVER = 8; // 0x8
field public static final int HEADSET = 1; // 0x1
- field public static final int HEALTH = 3; // 0x3
+ field public static final deprecated int HEALTH = 3; // 0x3
field public static final int HID_DEVICE = 19; // 0x13
field public static final int SAP = 10; // 0xa
field public static final int STATE_CONNECTED = 2; // 0x2
@@ -44846,6 +44848,7 @@
method public int compareTo(android.telephony.emergency.EmergencyNumber);
method public int describeContents();
method public java.lang.String getCountryIso();
+ method public int getEmergencyCallRouting();
method public int getEmergencyNumberSourceBitmask();
method public java.util.List<java.lang.Integer> getEmergencyNumberSources();
method public java.util.List<java.lang.Integer> getEmergencyServiceCategories();
@@ -44856,6 +44859,9 @@
method public boolean isInEmergencyServiceCategories(int);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.telephony.emergency.EmergencyNumber> CREATOR;
+ field public static final int EMERGENCY_CALL_ROUTING_EMERGENCY = 1; // 0x1
+ field public static final int EMERGENCY_CALL_ROUTING_NORMAL = 2; // 0x2
+ field public static final int EMERGENCY_CALL_ROUTING_UNKNOWN = 0; // 0x0
field public static final int EMERGENCY_NUMBER_SOURCE_DATABASE = 16; // 0x10
field public static final int EMERGENCY_NUMBER_SOURCE_DEFAULT = 8; // 0x8
field public static final int EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG = 4; // 0x4
diff --git a/api/system-current.txt b/api/system-current.txt
index 7688a15..79648a9 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -537,6 +537,13 @@
method public void onVrStateChanged(boolean);
}
+ public final class WallpaperColors implements android.os.Parcelable {
+ ctor public WallpaperColors(android.graphics.Color, android.graphics.Color, android.graphics.Color, int);
+ method public int getColorHints();
+ field public static final int HINT_SUPPORTS_DARK_TEXT = 1; // 0x1
+ field public static final int HINT_SUPPORTS_DARK_THEME = 2; // 0x2
+ }
+
public final class WallpaperInfo implements android.os.Parcelable {
method public boolean supportsAmbientMode();
}
@@ -5430,7 +5437,7 @@
method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
method public void onActionClicked(java.lang.String, android.app.Notification.Action, int);
method public final android.os.IBinder onBind(android.content.Intent);
- method public void onNotificationDirectReply(java.lang.String);
+ method public void onNotificationDirectReplied(java.lang.String);
method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, android.app.NotificationChannel);
method public void onNotificationExpansionChanged(java.lang.String, boolean, boolean);
@@ -6443,6 +6450,7 @@
method public deprecated boolean getDataEnabled(int);
method public boolean getEmergencyCallbackMode();
method public java.lang.String getIsimDomain();
+ method public java.lang.String getIsimIst();
method public int getPreferredNetworkTypeBitmap();
method public int getRadioPowerState();
method public int getSimApplicationState();
@@ -6843,6 +6851,7 @@
method public android.os.Bundle getCallExtras();
method public int getCallType();
method public static int getCallTypeFromVideoState(int);
+ method public int getEmergencyCallRouting();
method public int getEmergencyServiceCategories();
method public android.telephony.ims.ImsStreamMediaProfile getMediaProfile();
method public int getRestrictCause();
@@ -6856,6 +6865,7 @@
method public void setCallExtraBoolean(java.lang.String, boolean);
method public void setCallExtraInt(java.lang.String, int);
method public void setCallRestrictCause(int);
+ method public void setEmergencyCallRouting(int);
method public void setEmergencyServiceCategories(int);
method public void updateCallExtras(android.telephony.ims.ImsCallProfile);
method public void updateCallType(android.telephony.ims.ImsCallProfile);
@@ -6985,6 +6995,7 @@
public class ImsMmTelManager {
method public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(android.content.Context, int);
method public int getVoWiFiModeSetting();
+ method public int getVoWiFiRoamingModeSetting();
method public boolean isAdvancedCallingSettingEnabled();
method public boolean isAvailable(int, int);
method public boolean isCapable(int, int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 8ea7f20..ae3c1e0 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1237,7 +1237,7 @@
method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
method public void onActionClicked(java.lang.String, android.app.Notification.Action, int);
method public final android.os.IBinder onBind(android.content.Intent);
- method public void onNotificationDirectReply(java.lang.String);
+ method public void onNotificationDirectReplied(java.lang.String);
method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, android.app.NotificationChannel);
method public void onNotificationExpansionChanged(java.lang.String, boolean, boolean);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 5b8261e..fdb71bb 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2158,6 +2158,7 @@
* for helping activities determine the proper time to cancel a notification.
*
* @see #onUserInteraction()
+ * @see android.content.Intent#FLAG_ACTIVITY_NO_USER_ACTION
*/
protected void onUserLeaveHint() {
}
diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java
index ace814a..38a98d3c8 100644
--- a/core/java/android/app/WallpaperColors.java
+++ b/core/java/android/app/WallpaperColors.java
@@ -18,7 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.annotation.SystemApi;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -56,6 +56,7 @@
* eg. A launcher may set its text color to black if this flag is specified.
* @hide
*/
+ @SystemApi
public static final int HINT_SUPPORTS_DARK_TEXT = 1 << 0;
/**
@@ -64,6 +65,7 @@
* eg. A launcher may set its drawer color to black if this flag is specified.
* @hide
*/
+ @SystemApi
public static final int HINT_SUPPORTS_DARK_THEME = 1 << 1;
/**
@@ -234,7 +236,7 @@
* @see WallpaperColors#fromDrawable(Drawable)
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public WallpaperColors(@NonNull Color primaryColor, @Nullable Color secondaryColor,
@Nullable Color tertiaryColor, int colorHints) {
@@ -349,7 +351,7 @@
* @return True if dark text is supported.
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public int getColorHints() {
return mColorHints;
}
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 2990b57..e0a15a5 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -19,6 +19,7 @@
import static android.app.ActivityThread.isSystem;
import static android.app.WindowConfigurationProto.ACTIVITY_TYPE;
import static android.app.WindowConfigurationProto.APP_BOUNDS;
+import static android.app.WindowConfigurationProto.BOUNDS;
import static android.app.WindowConfigurationProto.WINDOWING_MODE;
import static android.view.Surface.rotationToString;
@@ -585,6 +586,9 @@
}
protoOutputStream.write(WINDOWING_MODE, mWindowingMode);
protoOutputStream.write(ACTIVITY_TYPE, mActivityType);
+ if (mBounds != null) {
+ mBounds.writeToProto(protoOutputStream, BOUNDS);
+ }
protoOutputStream.end(token);
}
@@ -606,6 +610,10 @@
mAppBounds = new Rect();
mAppBounds.readFromProto(proto, APP_BOUNDS);
break;
+ case (int) BOUNDS:
+ mBounds = new Rect();
+ mBounds.readFromProto(proto, BOUNDS);
+ break;
case (int) WINDOWING_MODE:
mWindowingMode = proto.readInt(WINDOWING_MODE);
break;
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 87b64797..1945b2f 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -2066,8 +2066,7 @@
* Get the current connection state of a profile.
* This function can be used to check whether the local Bluetooth adapter
* is connected to any remote device for a specific profile.
- * Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET},
- * {@link BluetoothProfile#A2DP}.
+ * Profile can be one of {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP}.
*
* <p> Return value can be one of
* {@link BluetoothProfile#STATE_DISCONNECTED},
@@ -2441,16 +2440,15 @@
/**
* Get the profile proxy object associated with the profile.
*
- * <p>Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET},
- * {@link BluetoothProfile#A2DP}, {@link BluetoothProfile#GATT}, or
- * {@link BluetoothProfile#GATT_SERVER}. Clients must implement
- * {@link BluetoothProfile.ServiceListener} to get notified of
- * the connection status and to get the proxy object.
+ * <p>Profile can be one of {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP},
+ * {@link BluetoothProfile#GATT}, or {@link BluetoothProfile#GATT_SERVER}. Clients must
+ * implement {@link BluetoothProfile.ServiceListener} to get notified of the connection status
+ * and to get the proxy object.
*
* @param context Context of the application
* @param listener The service Listener for connection callbacks.
- * @param profile The Bluetooth profile; either {@link BluetoothProfile#HEALTH}, {@link
- * BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP}. {@link BluetoothProfile#GATT} or
+ * @param profile The Bluetooth profile; either {@link BluetoothProfile#HEADSET},
+ * {@link BluetoothProfile#A2DP}. {@link BluetoothProfile#GATT} or
* {@link BluetoothProfile#GATT_SERVER}.
* @return true on success, false on error
*/
@@ -2479,8 +2477,8 @@
BluetoothPan pan = new BluetoothPan(context, listener);
return true;
} else if (profile == BluetoothProfile.HEALTH) {
- BluetoothHealth health = new BluetoothHealth(context, listener);
- return true;
+ Log.e(TAG, "getProfileProxy(): BluetoothHealth is deprecated");
+ return false;
} else if (profile == BluetoothProfile.MAP) {
BluetoothMap map = new BluetoothMap(context, listener);
return true;
@@ -2512,8 +2510,7 @@
*
* <p> Clients should call this when they are no longer using
* the proxy obtained from {@link #getProfileProxy}.
- * Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET} or
- * {@link BluetoothProfile#A2DP}
+ * Profile can be one of {@link BluetoothProfile#HEADSET} or {@link BluetoothProfile#A2DP}
*
* @param profile
* @param proxy Profile proxy object
@@ -2548,10 +2545,6 @@
BluetoothPan pan = (BluetoothPan) proxy;
pan.close();
break;
- case BluetoothProfile.HEALTH:
- BluetoothHealth health = (BluetoothHealth) proxy;
- health.close();
- break;
case BluetoothProfile.GATT:
BluetoothGatt gatt = (BluetoothGatt) proxy;
gatt.close();
diff --git a/core/java/android/bluetooth/BluetoothHealth.java b/core/java/android/bluetooth/BluetoothHealth.java
index 22d41d9..e2e56fd 100644
--- a/core/java/android/bluetooth/BluetoothHealth.java
+++ b/core/java/android/bluetooth/BluetoothHealth.java
@@ -16,15 +16,7 @@
package android.bluetooth;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.Binder;
-import android.os.IBinder;
import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -54,79 +46,59 @@
* <li> When done, close the health channel by calling {@link #disconnectChannel}
* and unregister the application configuration calling
* {@link #unregisterAppConfiguration}
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New apps
+ * should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
+@Deprecated
public final class BluetoothHealth implements BluetoothProfile {
private static final String TAG = "BluetoothHealth";
- private static final boolean DBG = true;
- private static final boolean VDBG = false;
-
/**
* Health Profile Source Role - the health device.
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
+ @Deprecated
public static final int SOURCE_ROLE = 1 << 0;
/**
* Health Profile Sink Role the device talking to the health device.
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
+ @Deprecated
public static final int SINK_ROLE = 1 << 1;
/**
* Health Profile - Channel Type used - Reliable
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
+ @Deprecated
public static final int CHANNEL_TYPE_RELIABLE = 10;
/**
* Health Profile - Channel Type used - Streaming
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
+ @Deprecated
public static final int CHANNEL_TYPE_STREAMING = 11;
- /**
- * @hide
- */
- public static final int CHANNEL_TYPE_ANY = 12;
-
- /** @hide */
- public static final int HEALTH_OPERATION_SUCCESS = 6000;
- /** @hide */
- public static final int HEALTH_OPERATION_ERROR = 6001;
- /** @hide */
- public static final int HEALTH_OPERATION_INVALID_ARGS = 6002;
- /** @hide */
- public static final int HEALTH_OPERATION_GENERIC_FAILURE = 6003;
- /** @hide */
- public static final int HEALTH_OPERATION_NOT_FOUND = 6004;
- /** @hide */
- public static final int HEALTH_OPERATION_NOT_ALLOWED = 6005;
-
- private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
- new IBluetoothStateChangeCallback.Stub() {
- public void onBluetoothStateChange(boolean up) {
- if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
- if (!up) {
- if (VDBG) Log.d(TAG, "Unbinding service...");
- synchronized (mConnection) {
- try {
- mService = null;
- mContext.unbindService(mConnection);
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- } else {
- synchronized (mConnection) {
- try {
- if (mService == null) {
- if (VDBG) Log.d(TAG, "Binding service...");
- doBind();
- }
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- }
- }
- };
-
/**
* Register an application configuration that acts as a Health SINK.
@@ -142,53 +114,17 @@
* @param callback A callback to indicate success or failure of the registration and all
* operations done on this application configuration.
* @return If true, callback will be called.
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
+ @Deprecated
public boolean registerSinkAppConfiguration(String name, int dataType,
BluetoothHealthCallback callback) {
- if (!isEnabled() || name == null) return false;
-
- if (VDBG) log("registerSinkApplication(" + name + ":" + dataType + ")");
- return registerAppConfiguration(name, dataType, SINK_ROLE,
- CHANNEL_TYPE_ANY, callback);
- }
-
- /**
- * Register an application configuration that acts as a Health SINK or in a Health
- * SOURCE role.This is an asynchronous call and so
- * the callback is used to notify success or failure if the function returns true.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @param name The friendly name associated with the application or configuration.
- * @param dataType The dataType of the Source role of Health Profile.
- * @param channelType The channel type. Will be one of {@link #CHANNEL_TYPE_RELIABLE} or {@link
- * #CHANNEL_TYPE_STREAMING}
- * @param callback - A callback to indicate success or failure.
- * @return If true, callback will be called.
- * @hide
- */
- public boolean registerAppConfiguration(String name, int dataType, int role,
- int channelType, BluetoothHealthCallback callback) {
- boolean result = false;
- if (!isEnabled() || !checkAppParam(name, role, channelType, callback)) return result;
-
- if (VDBG) log("registerApplication(" + name + ":" + dataType + ")");
- BluetoothHealthCallbackWrapper wrapper = new BluetoothHealthCallbackWrapper(callback);
- BluetoothHealthAppConfiguration config =
- new BluetoothHealthAppConfiguration(name, dataType, role, channelType);
-
- final IBluetoothHealth service = mService;
- if (service != null) {
- try {
- result = service.registerAppConfiguration(config, wrapper);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
- }
- return result;
+ Log.e(TAG, "registerSinkAppConfiguration(): BluetoothHealth is deprecated");
+ return false;
}
/**
@@ -199,22 +135,16 @@
*
* @param config The health app configuration
* @return Success or failure.
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
+ @Deprecated
public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) {
- boolean result = false;
- final IBluetoothHealth service = mService;
- if (service != null && isEnabled() && config != null) {
- try {
- result = service.unregisterAppConfiguration(config);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
- }
-
- return result;
+ Log.e(TAG, "unregisterAppConfiguration(): BluetoothHealth is deprecated");
+ return false;
}
/**
@@ -228,49 +158,16 @@
* @param config The application configuration which has been registered using {@link
* #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) }
* @return If true, the callback associated with the application config will be called.
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
+ @Deprecated
public boolean connectChannelToSource(BluetoothDevice device,
BluetoothHealthAppConfiguration config) {
- final IBluetoothHealth service = mService;
- if (service != null && isEnabled() && isValidDevice(device) && config != null) {
- try {
- return service.connectChannelToSource(device, config);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
- }
- return false;
- }
-
- /**
- * Connect to a health device which has the {@link #SINK_ROLE}.
- * This is an asynchronous call. If this function returns true, the callback
- * associated with the application configuration will be called.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @param device The remote Bluetooth device.
- * @param config The application configuration which has been registered using {@link
- * #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) }
- * @return If true, the callback associated with the application config will be called.
- * @hide
- */
- public boolean connectChannelToSink(BluetoothDevice device,
- BluetoothHealthAppConfiguration config, int channelType) {
- final IBluetoothHealth service = mService;
- if (service != null && isEnabled() && isValidDevice(device) && config != null) {
- try {
- return service.connectChannelToSink(device, config, channelType);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
- }
+ Log.e(TAG, "connectChannelToSource(): BluetoothHealth is deprecated");
return false;
}
@@ -286,20 +183,16 @@
* #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) }
* @param channelId The channel id associated with the channel
* @return If true, the callback associated with the application config will be called.
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
+ @Deprecated
public boolean disconnectChannel(BluetoothDevice device,
BluetoothHealthAppConfiguration config, int channelId) {
- final IBluetoothHealth service = mService;
- if (service != null && isEnabled() && isValidDevice(device) && config != null) {
- try {
- return service.disconnectChannel(device, config, channelId);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
- }
+ Log.e(TAG, "disconnectChannel(): BluetoothHealth is deprecated");
return false;
}
@@ -315,20 +208,16 @@
* @param device The remote Bluetooth health device
* @param config The application configuration
* @return null on failure, ParcelFileDescriptor on success.
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
+ @Deprecated
public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device,
BluetoothHealthAppConfiguration config) {
- final IBluetoothHealth service = mService;
- if (service != null && isEnabled() && isValidDevice(device) && config != null) {
- try {
- return service.getMainChannelFd(device, config);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
- }
+ Log.e(TAG, "getMainChannelFd(): BluetoothHealth is deprecated");
return null;
}
@@ -348,17 +237,7 @@
*/
@Override
public int getConnectionState(BluetoothDevice device) {
- final IBluetoothHealth service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
- try {
- return service.getHealthDeviceConnectionState(device);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
- }
+ Log.e(TAG, "getConnectionState(): BluetoothHealth is deprecated");
return STATE_DISCONNECTED;
}
@@ -378,17 +257,8 @@
*/
@Override
public List<BluetoothDevice> getConnectedDevices() {
- final IBluetoothHealth service = mService;
- if (service != null && isEnabled()) {
- try {
- return service.getConnectedHealthDevices();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
- }
- }
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ Log.e(TAG, "getConnectedDevices(): BluetoothHealth is deprecated");
+ return new ArrayList<>();
}
/**
@@ -410,163 +280,81 @@
*/
@Override
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
- final IBluetoothHealth service = mService;
- if (service != null && isEnabled()) {
- try {
- return service.getHealthDevicesMatchingConnectionStates(states);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
- }
- }
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ Log.e(TAG, "getDevicesMatchingConnectionStates(): BluetoothHealth is deprecated");
+ return new ArrayList<>();
}
- private static class BluetoothHealthCallbackWrapper extends IBluetoothHealthCallback.Stub {
- private BluetoothHealthCallback mCallback;
-
- public BluetoothHealthCallbackWrapper(BluetoothHealthCallback callback) {
- mCallback = callback;
- }
-
- @Override
- public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config,
- int status) {
- mCallback.onHealthAppConfigurationStatusChange(config, status);
- }
-
- @Override
- public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config,
- BluetoothDevice device, int prevState, int newState,
- ParcelFileDescriptor fd, int channelId) {
- mCallback.onHealthChannelStateChange(config, device, prevState, newState, fd,
- channelId);
- }
- }
-
- /** Health Channel Connection State - Disconnected */
+ /** Health Channel Connection State - Disconnected
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
+ */
+ @Deprecated
public static final int STATE_CHANNEL_DISCONNECTED = 0;
- /** Health Channel Connection State - Connecting */
+ /** Health Channel Connection State - Connecting
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
+ */
+ @Deprecated
public static final int STATE_CHANNEL_CONNECTING = 1;
- /** Health Channel Connection State - Connected */
+ /** Health Channel Connection State - Connected
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
+ */
+ @Deprecated
public static final int STATE_CHANNEL_CONNECTED = 2;
- /** Health Channel Connection State - Disconnecting */
+ /** Health Channel Connection State - Disconnecting
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
+ */
+ @Deprecated
public static final int STATE_CHANNEL_DISCONNECTING = 3;
- /** Health App Configuration registration success */
- public static final int APP_CONFIG_REGISTRATION_SUCCESS = 0;
- /** Health App Configuration registration failure */
- public static final int APP_CONFIG_REGISTRATION_FAILURE = 1;
- /** Health App Configuration un-registration success */
- public static final int APP_CONFIG_UNREGISTRATION_SUCCESS = 2;
- /** Health App Configuration un-registration failure */
- public static final int APP_CONFIG_UNREGISTRATION_FAILURE = 3;
-
- private Context mContext;
- private ServiceListener mServiceListener;
- private volatile IBluetoothHealth mService;
- BluetoothAdapter mAdapter;
-
- /**
- * Create a BluetoothHealth proxy object.
+ /** Health App Configuration registration success
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
- /*package*/ BluetoothHealth(Context context, ServiceListener l) {
- mContext = context;
- mServiceListener = l;
- mAdapter = BluetoothAdapter.getDefaultAdapter();
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- }
- }
-
- doBind();
- }
-
- boolean doBind() {
- Intent intent = new Intent(IBluetoothHealth.class.getName());
- ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
- intent.setComponent(comp);
- if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- UserHandle.CURRENT_OR_SELF)) {
- Log.e(TAG, "Could not bind to Bluetooth Health Service with " + intent);
- return false;
- }
- return true;
- }
-
- /*package*/ void close() {
- if (VDBG) log("close()");
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (Exception e) {
- Log.e(TAG, "", e);
- }
- }
-
- synchronized (mConnection) {
- if (mService != null) {
- try {
- mService = null;
- mContext.unbindService(mConnection);
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- }
- mServiceListener = null;
- }
-
- private final ServiceConnection mConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder service) {
- if (DBG) Log.d(TAG, "Proxy object connected");
- mService = IBluetoothHealth.Stub.asInterface(Binder.allowBlocking(service));
-
- if (mServiceListener != null) {
- mServiceListener.onServiceConnected(BluetoothProfile.HEALTH, BluetoothHealth.this);
- }
- }
-
- public void onServiceDisconnected(ComponentName className) {
- if (DBG) Log.d(TAG, "Proxy object disconnected");
- mService = null;
- if (mServiceListener != null) {
- mServiceListener.onServiceDisconnected(BluetoothProfile.HEALTH);
- }
- }
- };
-
- private boolean isEnabled() {
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-
- if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true;
- log("Bluetooth is Not enabled");
- return false;
- }
-
- private static boolean isValidDevice(BluetoothDevice device) {
- return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
- }
-
- private boolean checkAppParam(String name, int role, int channelType,
- BluetoothHealthCallback callback) {
- if (name == null || (role != SOURCE_ROLE && role != SINK_ROLE)
- || (channelType != CHANNEL_TYPE_RELIABLE && channelType != CHANNEL_TYPE_STREAMING
- && channelType != CHANNEL_TYPE_ANY)
- || callback == null) {
- return false;
- }
- if (role == SOURCE_ROLE && channelType == CHANNEL_TYPE_ANY) return false;
- return true;
- }
-
- private static void log(String msg) {
- Log.d(TAG, msg);
- }
+ @Deprecated
+ public static final int APP_CONFIG_REGISTRATION_SUCCESS = 0;
+ /** Health App Configuration registration failure
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
+ */
+ @Deprecated
+ public static final int APP_CONFIG_REGISTRATION_FAILURE = 1;
+ /** Health App Configuration un-registration success
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
+ */
+ @Deprecated
+ public static final int APP_CONFIG_UNREGISTRATION_SUCCESS = 2;
+ /** Health App Configuration un-registration failure
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
+ */
+ @Deprecated
+ public static final int APP_CONFIG_UNREGISTRATION_FAILURE = 3;
}
diff --git a/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java b/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java
index 7c9db6f..9788bbf 100644
--- a/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java
+++ b/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java
@@ -25,72 +25,14 @@
* the {@link BluetoothHealth} class. This class represents an application configuration
* that the Bluetooth Health third party application will register to communicate with the
* remote Bluetooth health device.
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
+@Deprecated
public final class BluetoothHealthAppConfiguration implements Parcelable {
- private final String mName;
- private final int mDataType;
- private final int mRole;
- private final int mChannelType;
-
- /**
- * Constructor to register the SINK role
- *
- * @param name Friendly name associated with the application configuration
- * @param dataType Data Type of the remote Bluetooth Health device
- * @hide
- */
- BluetoothHealthAppConfiguration(String name, int dataType) {
- mName = name;
- mDataType = dataType;
- mRole = BluetoothHealth.SINK_ROLE;
- mChannelType = BluetoothHealth.CHANNEL_TYPE_ANY;
- }
-
- /**
- * Constructor to register the application configuration.
- *
- * @param name Friendly name associated with the application configuration
- * @param dataType Data Type of the remote Bluetooth Health device
- * @param role {@link BluetoothHealth#SOURCE_ROLE} or {@link BluetoothHealth#SINK_ROLE}
- * @hide
- */
- BluetoothHealthAppConfiguration(String name, int dataType, int role, int
- channelType) {
- mName = name;
- mDataType = dataType;
- mRole = role;
- mChannelType = channelType;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof BluetoothHealthAppConfiguration) {
- BluetoothHealthAppConfiguration config = (BluetoothHealthAppConfiguration) o;
-
- if (mName == null) return false;
-
- return mName.equals(config.getName()) && mDataType == config.getDataType()
- && mRole == config.getRole() && mChannelType == config.getChannelType();
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- int result = 17;
- result = 31 * result + (mName != null ? mName.hashCode() : 0);
- result = 31 * result + mDataType;
- result = 31 * result + mRole;
- result = 31 * result + mChannelType;
- return result;
- }
-
- @Override
- public String toString() {
- return "BluetoothHealthAppConfiguration [mName = " + mName + ",mDataType = " + mDataType
- + ", mRole = " + mRole + ",mChannelType = " + mChannelType + "]";
- }
-
@Override
public int describeContents() {
return 0;
@@ -100,50 +42,59 @@
* Return the data type associated with this application configuration.
*
* @return dataType
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
+ @Deprecated
public int getDataType() {
- return mDataType;
+ return 0;
}
/**
* Return the name of the application configuration.
*
* @return String name
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
+ @Deprecated
public String getName() {
- return mName;
+ return null;
}
/**
* Return the role associated with this application configuration.
*
* @return One of {@link BluetoothHealth#SOURCE_ROLE} or {@link BluetoothHealth#SINK_ROLE}
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
+ @Deprecated
public int getRole() {
- return mRole;
+ return 0;
}
/**
- * Return the channel type associated with this application configuration.
- *
- * @return One of {@link BluetoothHealth#CHANNEL_TYPE_RELIABLE} or {@link
- * BluetoothHealth#CHANNEL_TYPE_STREAMING} or {@link BluetoothHealth#CHANNEL_TYPE_ANY}.
- * @hide
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
- public int getChannelType() {
- return mChannelType;
- }
-
+ @Deprecated
public static final Parcelable.Creator<BluetoothHealthAppConfiguration> CREATOR =
new Parcelable.Creator<BluetoothHealthAppConfiguration>() {
@Override
public BluetoothHealthAppConfiguration createFromParcel(Parcel in) {
- String name = in.readString();
- int type = in.readInt();
- int role = in.readInt();
- int channelType = in.readInt();
- return new BluetoothHealthAppConfiguration(name, type, role,
- channelType);
+ return new BluetoothHealthAppConfiguration();
}
@Override
@@ -153,10 +104,5 @@
};
@Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(mName);
- out.writeInt(mDataType);
- out.writeInt(mRole);
- out.writeInt(mChannelType);
- }
+ public void writeToParcel(Parcel out, int flags) {}
}
diff --git a/core/java/android/bluetooth/BluetoothHealthCallback.java b/core/java/android/bluetooth/BluetoothHealthCallback.java
index 4023485..4769212 100644
--- a/core/java/android/bluetooth/BluetoothHealthCallback.java
+++ b/core/java/android/bluetooth/BluetoothHealthCallback.java
@@ -23,7 +23,13 @@
/**
* This abstract class is used to implement {@link BluetoothHealth} callbacks.
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
+@Deprecated
public abstract class BluetoothHealthCallback {
private static final String TAG = "BluetoothHealthCallback";
@@ -38,8 +44,14 @@
* BluetoothHealth#APP_CONFIG_REGISTRATION_FAILURE} or
* {@link BluetoothHealth#APP_CONFIG_UNREGISTRATION_SUCCESS}
* or {@link BluetoothHealth#APP_CONFIG_UNREGISTRATION_FAILURE}
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
@BinderThread
+ @Deprecated
public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config,
int status) {
Log.d(TAG, "onHealthAppConfigurationStatusChange: " + config + "Status: " + status);
@@ -58,8 +70,14 @@
* @param fd The Parcel File Descriptor when the channel state is connected.
* @param channelId The id associated with the channel. This id will be used in future calls
* like when disconnecting the channel.
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
@BinderThread
+ @Deprecated
public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config,
BluetoothDevice device, int prevState, int newState, ParcelFileDescriptor fd,
int channelId) {
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index 3c3a01b..3c87c73 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -72,7 +72,13 @@
/**
* Health Profile
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
+ @Deprecated
int HEALTH = 3;
/**
@@ -269,9 +275,8 @@
* Called to notify the client when the proxy object has been
* connected to the service.
*
- * @param profile - One of {@link #HEALTH}, {@link #HEADSET} or {@link #A2DP}
- * @param proxy - One of {@link BluetoothHealth}, {@link BluetoothHeadset} or {@link
- * BluetoothA2dp}
+ * @param profile - One of {@link #HEADSET} or {@link #A2DP}
+ * @param proxy - One of {@link BluetoothHeadset} or {@link BluetoothA2dp}
*/
public void onServiceConnected(int profile, BluetoothProfile proxy);
@@ -279,7 +284,7 @@
* Called to notify the client that this proxy object has been
* disconnected from the service.
*
- * @param profile - One of {@link #HEALTH}, {@link #HEADSET} or {@link #A2DP}
+ * @param profile - One of {@link #HEADSET} or {@link #A2DP}
*/
public void onServiceDisconnected(int profile);
}
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 3a79206..4a466f3 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -55,6 +55,8 @@
private String mIfaceName;
private ArrayList<LinkAddress> mLinkAddresses = new ArrayList<>();
private ArrayList<InetAddress> mDnses = new ArrayList<>();
+ // PCSCF addresses are addresses of SIP proxies that only exist for the IMS core service.
+ private ArrayList<InetAddress> mPcscfs = new ArrayList<InetAddress>();
private ArrayList<InetAddress> mValidatedPrivateDnses = new ArrayList<>();
private boolean mUsePrivateDns;
private String mPrivateDnsServerName;
@@ -179,6 +181,7 @@
mValidatedPrivateDnses.addAll(source.mValidatedPrivateDnses);
mUsePrivateDns = source.mUsePrivateDns;
mPrivateDnsServerName = source.mPrivateDnsServerName;
+ mPcscfs.addAll(source.mPcscfs);
mDomains = source.mDomains;
mRoutes.addAll(source.mRoutes);
mHttpProxy = (source.mHttpProxy == null) ? null : new ProxyInfo(source.mHttpProxy);
@@ -525,6 +528,60 @@
}
/**
+ * Adds the given {@link InetAddress} to the list of PCSCF servers, if not present.
+ *
+ * @param pcscfServer The {@link InetAddress} to add to the list of PCSCF servers.
+ * @return true if the PCSCF server was added, false otherwise.
+ * @hide
+ */
+ public boolean addPcscfServer(InetAddress pcscfServer) {
+ if (pcscfServer != null && !mPcscfs.contains(pcscfServer)) {
+ mPcscfs.add(pcscfServer);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Removes the given {@link InetAddress} from the list of PCSCF servers.
+ *
+ * @param pcscf Server The {@link InetAddress} to remove from the list of PCSCF servers.
+ * @return true if the PCSCF server was removed, false otherwise.
+ * @hide
+ */
+ public boolean removePcscfServer(InetAddress pcscfServer) {
+ if (pcscfServer != null) {
+ return mPcscfs.remove(pcscfServer);
+ }
+ return false;
+ }
+
+ /**
+ * Replaces the PCSCF servers in this {@code LinkProperties} with
+ * the given {@link Collection} of {@link InetAddress} objects.
+ *
+ * @param addresses The {@link Collection} of PCSCF servers to set in this object.
+ * @hide
+ */
+ public void setPcscfServers(Collection<InetAddress> pcscfServers) {
+ mPcscfs.clear();
+ for (InetAddress pcscfServer: pcscfServers) {
+ addPcscfServer(pcscfServer);
+ }
+ }
+
+ /**
+ * Returns all the {@link InetAddress} for PCSCF servers on this link.
+ *
+ * @return An unmodifiable {@link List} of {@link InetAddress} for PCSCF servers on
+ * this link.
+ * @hide
+ */
+ public List<InetAddress> getPcscfServers() {
+ return Collections.unmodifiableList(mPcscfs);
+ }
+
+ /**
* Sets the DNS domain search path used on this link.
*
* @param domains A {@link String} listing in priority order the comma separated
@@ -767,6 +824,7 @@
mDnses.clear();
mUsePrivateDns = false;
mPrivateDnsServerName = null;
+ mPcscfs.clear();
mDomains = null;
mRoutes.clear();
mHttpProxy = null;
@@ -813,6 +871,12 @@
resultJoiner.add(mPrivateDnsServerName);
}
+ if (!mPcscfs.isEmpty()) {
+ resultJoiner.add("PcscfAddresses: [");
+ resultJoiner.add(TextUtils.join(",", mPcscfs));
+ resultJoiner.add("]");
+ }
+
if (!mValidatedPrivateDnses.isEmpty()) {
final StringJoiner validatedPrivateDnsesJoiner =
new StringJoiner(",", "ValidatedPrivateDnsAddresses: [", "]");
@@ -965,6 +1029,36 @@
}
/**
+ * Returns true if this link has an IPv4 PCSCF server.
+ *
+ * @return {@code true} if there is an IPv4 PCSCF server, {@code false} otherwise.
+ * @hide
+ */
+ public boolean hasIPv4PcscfServer() {
+ for (InetAddress ia : mPcscfs) {
+ if (ia instanceof Inet4Address) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if this link has an IPv6 PCSCF server.
+ *
+ * @return {@code true} if there is an IPv6 PCSCF server, {@code false} otherwise.
+ * @hide
+ */
+ public boolean hasIPv6PcscfServer() {
+ for (InetAddress ia : mPcscfs) {
+ if (ia instanceof Inet6Address) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* Returns true if this link is provisioned for global IPv4 connectivity.
* This requires an IP address, default route, and DNS server.
*
@@ -1117,6 +1211,19 @@
}
/**
+ * Compares this {@code LinkProperties} PCSCF addresses against the target
+ *
+ * @param target LinkProperties to compare.
+ * @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
+ */
+ public boolean isIdenticalPcscfs(LinkProperties target) {
+ Collection<InetAddress> targetPcscfs = target.getPcscfServers();
+ return (mPcscfs.size() == targetPcscfs.size()) ?
+ mPcscfs.containsAll(targetPcscfs) : false;
+ }
+
+ /**
* Compares this {@code LinkProperties} Routes against the target
*
* @param target LinkProperties to compare.
@@ -1218,6 +1325,7 @@
&& isIdenticalDnses(target)
&& isIdenticalPrivateDns(target)
&& isIdenticalValidatedPrivateDnses(target)
+ && isIdenticalPcscfs(target)
&& isIdenticalRoutes(target)
&& isIdenticalHttpProxy(target)
&& isIdenticalStackedLinks(target)
@@ -1334,6 +1442,7 @@
+ mMtu * 51
+ ((null == mTcpBufferSizes) ? 0 : mTcpBufferSizes.hashCode())
+ (mUsePrivateDns ? 57 : 0)
+ + mPcscfs.size() * 67
+ ((null == mPrivateDnsServerName) ? 0 : mPrivateDnsServerName.hashCode());
}
@@ -1357,6 +1466,10 @@
}
dest.writeBoolean(mUsePrivateDns);
dest.writeString(mPrivateDnsServerName);
+ dest.writeInt(mPcscfs.size());
+ for (InetAddress d : mPcscfs) {
+ dest.writeByteArray(d.getAddress());
+ }
dest.writeString(mDomains);
dest.writeInt(mMtu);
dest.writeString(mTcpBufferSizes);
@@ -1406,6 +1519,12 @@
}
netProp.setUsePrivateDns(in.readBoolean());
netProp.setPrivateDnsServerName(in.readString());
+ addressCount = in.readInt();
+ for (int i = 0; i < addressCount; i++) {
+ try {
+ netProp.addPcscfServer(InetAddress.getByAddress(in.createByteArray()));
+ } catch (UnknownHostException e) { }
+ }
netProp.setDomains(in.readString());
netProp.setMtu(in.readInt());
netProp.setTcpBufferSizes(in.readString());
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 4c0ee6f..b0b8f49 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -353,6 +353,26 @@
}
/**
+ * Attempt to setup ANGLE with a (temporary) default rules file: b/121153494
+ * True: Rules file was loaded.
+ * False: Rules file was *not* loaded.
+ */
+ private boolean setupAngleRulesDebug(String packageName, String paths, String devOptIn) {
+ // b/121153494
+ // Skip APK rules file checking.
+ if (!DEBUG) {
+ Log.v(TAG, "Skipping loading the rules file.");
+ // Fill in some default values for now, so the loader can get an answer when it asks.
+ // Most importantly, we need to indicate which app we are init'ing and what the
+ // developer options for it are so we can turn on ANGLE if needed.
+ setAngleInfo(paths, packageName, devOptIn, null, 0, 0);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
* Attempt to setup ANGLE with a rules file loaded from the ANGLE APK.
* True: APK rules file was loaded.
* False: APK rules file was *not* loaded.
@@ -430,6 +450,12 @@
return;
}
+ // b/121153494
+ if (setupAngleRulesDebug(packageName, paths, devOptIn)) {
+ // We setup ANGLE with defaults, so we're done here.
+ return;
+ }
+
if (setupAngleRulesApk(anglePkgName, angleInfo, context, packageName, paths, devOptIn)) {
// We setup ANGLE with rules from the APK, so we're done here.
return;
@@ -454,14 +480,19 @@
return;
}
- String applicationPackageName = context.getPackageName();
- String devOptInApplicationName = coreSettings.getString(
- Settings.Global.GUP_DEV_OPT_IN_APPS);
- boolean devOptIn = applicationPackageName.equals(devOptInApplicationName);
- boolean whitelisted = onWhitelist(context, driverPackageName, ai.packageName);
- if (!devOptIn && !whitelisted) {
+ if (getGlobalSettingsString(coreSettings, Settings.Global.GUP_DEV_OPT_OUT_APPS)
+ .contains(ai.packageName)) {
if (DEBUG) {
- Log.w(TAG, applicationPackageName + " is not on the whitelist.");
+ Log.w(TAG, ai.packageName + " opts out from GUP.");
+ }
+ return;
+ }
+
+ if (!getGlobalSettingsString(coreSettings, Settings.Global.GUP_DEV_OPT_IN_APPS)
+ .contains(ai.packageName)
+ && !onWhitelist(context, driverPackageName, ai.packageName)) {
+ if (DEBUG) {
+ Log.w(TAG, ai.packageName + " is not on the whitelist.");
}
return;
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 5bb5ee8..66e1c80 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12000,12 +12000,18 @@
"angle_gl_driver_selection_values";
/**
- * Apps that are selected to use Game Update Package.
+ * List of Apps selected to use Game Update Packages.
* @hide
*/
public static final String GUP_DEV_OPT_IN_APPS = "gup_dev_opt_in_apps";
/**
+ * List of Apps selected not to use Game Update Packages.
+ * @hide
+ */
+ public static final String GUP_DEV_OPT_OUT_APPS = "gup_dev_opt_out_apps";
+
+ /**
* Apps on the black list that are forbidden to useGame Update Package.
* @hide
*/
@@ -13829,6 +13835,7 @@
* enabled (boolean)
* requires_targeting_p (boolean)
* max_squeeze_remeasure_attempts (int)
+ * edit_choices_before_sending (boolean)
* </pre>
* @see com.android.systemui.statusbar.policy.SmartReplyConstants
* @hide
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index dd0242a..fd2b0fae 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -185,7 +185,7 @@
* Implement this to know when a direct reply is sent from a notification.
* @param key the notification key
*/
- public void onNotificationDirectReply(@NonNull String key) {}
+ public void onNotificationDirectReplied(@NonNull String key) {}
/**
* Implement this to know when a suggested reply is sent.
@@ -407,7 +407,7 @@
SomeArgs args = (SomeArgs) msg.obj;
String key = (String) args.arg1;
args.recycle();
- onNotificationDirectReply(key);
+ onNotificationDirectReplied(key);
break;
}
case MSG_ON_SUGGESTED_REPLY_SENT: {
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 0b6beba..acad5d7 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -158,7 +158,6 @@
private static native void nativeSeverChildren(long transactionObj, long nativeObject);
private static native void nativeSetOverrideScalingMode(long transactionObj, long nativeObject,
int scalingMode);
- private static native void nativeDestroy(long transactionObj, long nativeObject);
private static native IBinder nativeGetHandle(long nativeObject);
private static native boolean nativeGetTransformToDisplayInverse(long nativeObject);
@@ -360,11 +359,18 @@
*/
public static final int WINDOW_TYPE_DONT_SCREENSHOT = 441731;
+ private void assignNativeObject(long nativeObject) {
+ if (mNativeObject != 0) {
+ release();
+ }
+ mNativeObject = nativeObject;
+ }
+
public void copyFrom(SurfaceControl other) {
mName = other.mName;
mWidth = other.mWidth;
mHeight = other.mHeight;
- mNativeObject = nativeCopyFromSurfaceControl(other.mNativeObject);
+ assignNativeObject(nativeCopyFromSurfaceControl(other.mNativeObject));
}
/**
@@ -685,12 +691,11 @@
mWidth = in.readInt();
mHeight = in.readInt();
- release();
+ long object = 0;
if (in.readInt() != 0) {
- mNativeObject = nativeReadFromParcel(in);
- } else {
- mNativeObject = 0;
+ object = nativeReadFromParcel(in);
}
+ assignNativeObject(object);
}
@Override
@@ -1765,30 +1770,6 @@
return this;
}
- /**
- * Same as {@link #destroy()} except this is invoked in a transaction instead of
- * immediately.
- */
- public Transaction destroy(SurfaceControl sc) {
- sc.checkNotReleased();
-
- /**
- * Perhaps it's safer to transfer the close guard to the Transaction
- * but then we have a whole wonky scenario regarding merging, multiple
- * close-guards per transaction etc...the whole scenario is kind of wonky
- * and it seems really we'd like to just be able to call release here
- * but the WindowManager has some code that looks like
- * --- destroyInTransaction(a)
- * --- reparentChildrenInTransaction(a)
- * so we need to ensure the SC remains valid until the transaction
- * is applied.
- */
- sc.mCloseGuard.close();
-
- nativeDestroy(mNativeObject, sc.mNativeObject);
- return this;
- }
-
public Transaction setDisplaySurface(IBinder displayToken, Surface surface) {
if (displayToken == null) {
throw new IllegalArgumentException("displayToken must not be null");
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 20febc2..afe46701 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -837,8 +837,8 @@
mContentWidth = width;
mContentHeight = height;
- mOffsetX = (int) (0.1f * width);
- mOffsetY = (int) (0.1f * height);
+ mOffsetX = (int) (1.05f * elevation);
+ mOffsetY = (int) (1.05f * elevation);
// Setup the surface we will use for drawing the content and shadow.
mSurfaceWidth = mContentWidth + 2 * mOffsetX;
mSurfaceHeight = mContentHeight + 2 * mOffsetY;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 0ea8579..7f63653 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -3520,7 +3520,7 @@
*/
@android.view.RemotableViewMethod
public void setTextSelectHandle(@DrawableRes int textSelectHandle) {
- Preconditions.checkArgumentPositive(textSelectHandle,
+ Preconditions.checkArgument(textSelectHandle != 0,
"The text select handle should be a valid drawable resource id.");
setTextSelectHandle(mContext.getDrawable(textSelectHandle));
}
@@ -3577,7 +3577,7 @@
*/
@android.view.RemotableViewMethod
public void setTextSelectHandleLeft(@DrawableRes int textSelectHandleLeft) {
- Preconditions.checkArgumentPositive(textSelectHandleLeft,
+ Preconditions.checkArgument(textSelectHandleLeft != 0,
"The text select left handle should be a valid drawable resource id.");
setTextSelectHandleLeft(mContext.getDrawable(textSelectHandleLeft));
}
@@ -3634,7 +3634,7 @@
*/
@android.view.RemotableViewMethod
public void setTextSelectHandleRight(@DrawableRes int textSelectHandleRight) {
- Preconditions.checkArgumentPositive(textSelectHandleRight,
+ Preconditions.checkArgument(textSelectHandleRight != 0,
"The text select right handle should be a valid drawable resource id.");
setTextSelectHandleRight(mContext.getDrawable(textSelectHandleRight));
}
@@ -3667,9 +3667,7 @@
* @see #setTextCursorDrawable(int)
* @attr ref android.R.styleable#TextView_textCursorDrawable
*/
- public void setTextCursorDrawable(@NonNull Drawable textCursorDrawable) {
- Preconditions.checkNotNull(textCursorDrawable,
- "The cursor drawable should not be null.");
+ public void setTextCursorDrawable(@Nullable Drawable textCursorDrawable) {
mCursorDrawable = textCursorDrawable;
mCursorDrawableRes = 0;
if (mEditor != null) {
@@ -3687,8 +3685,6 @@
* @attr ref android.R.styleable#TextView_textCursorDrawable
*/
public void setTextCursorDrawable(@DrawableRes int textCursorDrawable) {
- Preconditions.checkArgumentPositive(textCursorDrawable,
- "The cursor drawable should be a valid drawable resource id.");
setTextCursorDrawable(mContext.getDrawable(textCursorDrawable));
}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index e2c23de..cdaf33f 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -232,7 +232,7 @@
// This is the caption view for the window, containing the caption and window control
// buttons. The visibility of this decor depends on the workspace and the window type.
// If the window type does not require such a view, this member might be null.
- DecorCaptionView mDecorCaptionView;
+ private DecorCaptionView mDecorCaptionView;
private boolean mWindowResizeCallbacksAdded = false;
private Drawable.Callback mLastBackgroundDrawableCb = null;
@@ -977,6 +977,7 @@
@Override
public void onWindowSystemUiVisibilityChanged(int visible) {
updateColorViews(null /* insets */, true /* animate */);
+ updateDecorCaptionStatus(getResources().getConfiguration());
}
@Override
@@ -1855,8 +1856,27 @@
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- final boolean displayWindowDecor =
- newConfig.windowConfiguration.hasWindowDecorCaption();
+ updateDecorCaptionStatus(newConfig);
+
+ updateAvailableWidth();
+ initializeElevation();
+ }
+
+ /**
+ * Determines if the workspace is entirely covered by the window.
+ * @return {@code true} when the window is filling the entire screen/workspace.
+ **/
+ private boolean isFillingScreen(Configuration config) {
+ final boolean isFullscreen = config.windowConfiguration.getWindowingMode()
+ == WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+ return isFullscreen && (0 != ((getWindowSystemUiVisibility() | getSystemUiVisibility())
+ & (View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_LOW_PROFILE)));
+ }
+
+ private void updateDecorCaptionStatus(Configuration config) {
+ final boolean displayWindowDecor = config.windowConfiguration.hasWindowDecorCaption()
+ && !isFillingScreen(config);
if (mDecorCaptionView == null && displayWindowDecor) {
// Configuration now requires a caption.
final LayoutInflater inflater = mWindow.getLayoutInflater();
@@ -1875,9 +1895,6 @@
mDecorCaptionView.onConfigurationChanged(displayWindowDecor);
enableCaption(displayWindowDecor);
}
-
- updateAvailableWidth();
- initializeElevation();
}
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
diff --git a/core/java/com/android/internal/widget/DecorCaptionView.java b/core/java/com/android/internal/widget/DecorCaptionView.java
index b419113..21558d3 100644
--- a/core/java/com/android/internal/widget/DecorCaptionView.java
+++ b/core/java/com/android/internal/widget/DecorCaptionView.java
@@ -319,23 +319,12 @@
mOwner.notifyRestrictedCaptionAreaCallback(mMaximize.getLeft(), mMaximize.getTop(),
mClose.getRight(), mClose.getBottom());
}
- /**
- * Determine if the workspace is entirely covered by the window.
- * @return Returns true when the window is filling the entire screen/workspace.
- **/
- private boolean isFillingScreen() {
- return (0 != ((getWindowSystemUiVisibility() | getSystemUiVisibility()) &
- (View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
- View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_LOW_PROFILE)));
- }
/**
* Updates the visibility of the caption.
**/
private void updateCaptionVisibility() {
- // Don't show the caption if the window has e.g. entered full screen.
- boolean invisible = isFillingScreen() || !mShow;
- mCaption.setVisibility(invisible ? GONE : VISIBLE);
+ mCaption.setVisibility(mShow ? VISIBLE : GONE);
mCaption.setOnTouchListener(this);
}
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index f292d25..9ce6df1 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -870,14 +870,6 @@
transaction->setOverrideScalingMode(ctrl, scalingMode);
}
-static void nativeDestroyInTransaction(JNIEnv* env, jclass clazz,
- jlong transactionObj,
- jlong nativeObject) {
- auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
- auto ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
- transaction->destroySurface(ctrl);
-}
-
static jobject nativeGetHandle(JNIEnv* env, jclass clazz, jlong nativeObject) {
auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
return javaObjectForIBinder(env, ctrl->getHandle());
@@ -1048,8 +1040,6 @@
(void*)nativeSeverChildren } ,
{"nativeSetOverrideScalingMode", "(JJI)V",
(void*)nativeSetOverrideScalingMode },
- {"nativeDestroy", "(JJ)V",
- (void*)nativeDestroyInTransaction },
{"nativeGetHandle", "(J)Landroid/os/IBinder;",
(void*)nativeGetHandle },
{"nativeScreenshot", "(Landroid/os/IBinder;Landroid/graphics/Rect;IIZI)Landroid/graphics/GraphicBuffer;",
diff --git a/core/proto/android/app/window_configuration.proto b/core/proto/android/app/window_configuration.proto
index 2d15552..6cc1a40 100644
--- a/core/proto/android/app/window_configuration.proto
+++ b/core/proto/android/app/window_configuration.proto
@@ -29,4 +29,5 @@
optional .android.graphics.RectProto app_bounds = 1;
optional int32 windowing_mode = 2;
optional int32 activity_type = 3;
+ optional .android.graphics.RectProto bounds = 4;
}
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index d817aa3..a00fde9 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -436,11 +436,12 @@
// Ordered GPU debug layer list for GLES
// i.e. <layer1>:<layer2>:...:<layerN>
optional SettingProto debug_layers_gles = 7;
- // Apps opt in to load graphics driver from Game Update Package
- // instead of native graphcis driver through developer options.
+ // GUP - List of Apps selected to use Game Update Packages
optional SettingProto gup_dev_opt_in_apps = 8;
- // Apps on the black list that are forbidden to useGame Update Package.
- optional SettingProto gup_black_list = 9;
+ // GUP - List of Apps selected not to use Game Update Packages
+ optional SettingProto gup_dev_opt_out_apps = 9;
+ // GUP - List of Apps that are forbidden to use Game Update Packages
+ optional SettingProto gup_black_list = 10;
}
optional Gpu gpu = 59;
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 70267c7..f863356 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -478,6 +478,7 @@
Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS,
Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES,
Settings.Global.GUP_DEV_OPT_IN_APPS,
+ Settings.Global.GUP_DEV_OPT_OUT_APPS,
Settings.Global.GUP_BLACK_LIST,
Settings.Global.GPU_DEBUG_LAYER_APP,
Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING,
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 04bf804..21531ab 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -99,6 +99,46 @@
<font weight="400" style="normal">CarroisGothicSC-Regular.ttf</font>
</family>
+ <family name="arbutus-slab">
+ <font weight="400" style="normal">ArbutusSlab-Regular.ttf</font>
+ </family>
+
+ <family name="arvo">
+ <font weight="400" style="normal">Arvo-Regular.ttf</font>
+ <font weight="400" style="italic">Arvo-Italic.ttf</font>
+ <font weight="700" style="normal">Arvo-Bold.ttf</font>
+ <font weight="700" style="italic">Arvo-BoldItalic.ttf</font>
+ </family>
+ <alias name="arvo-bold" to="arvo" weight="700" />
+
+ <family name="lato">
+ <font weight="400" style="normal">Lato-Regular.ttf</font>
+ <font weight="400" style="italic">Lato-Italic.ttf</font>
+ <font weight="700" style="normal">Lato-Bold.ttf</font>
+ <font weight="700" style="italic">Lato-BoldItalic.ttf</font>
+ </family>
+ <alias name="lato-bold" to="lato" weight="700" />
+
+ <family name="rubik">
+ <font weight="400" style="normal">Rubik-Regular.ttf</font>
+ <font weight="400" style="italic">Rubik-Italic.ttf</font>
+ <font weight="500" style="normal">Rubik-Medium.ttf</font>
+ <font weight="500" style="italic">Rubik-MediumItalic.ttf</font>
+ <font weight="700" style="normal">Rubik-Bold.ttf</font>
+ <font weight="700" style="italic">Rubik-BoldItalic.ttf</font>
+ </family>
+ <alias name="rubik-medium" to="rubik" weight="500" />
+
+ <family name="source-sans-pro">
+ <font weight="400" style="normal">SourceSansPro-Regular.ttf</font>
+ <font weight="400" style="italic">SourceSansPro-Italic.ttf</font>
+ <font weight="600" style="normal">SourceSansPro-SemiBold.ttf</font>
+ <font weight="600" style="italic">SourceSansPro-SemiBoldItalic.ttf</font>
+ <font weight="700" style="normal">SourceSansPro-Bold.ttf</font>
+ <font weight="700" style="italic">SourceSansPro-BoldItalic.ttf</font>
+ </family>
+ <alias name="source-sans-pro-semi-bold" to="source-sans-pro" weight="600" />
+
<!-- fallback fonts -->
<family lang="und-Arab" variant="elegant">
<font weight="400" style="normal">NotoNaskhArabic-Regular.ttf</font>
diff --git a/drm/java/android/drm/DrmManagerClient.java b/drm/java/android/drm/DrmManagerClient.java
index 76eab4a..fcebad3 100644
--- a/drm/java/android/drm/DrmManagerClient.java
+++ b/drm/java/android/drm/DrmManagerClient.java
@@ -831,6 +831,7 @@
* content://media/<table_name>/<row_index> (or)
* file://sdcard/test.mp4
* http://test.com/test.mp4
+ * https://test.com/test.mp4
*
* Here <table_name> shall be "video" or "audio" or "images"
* <row_index> the index of the content in given table
@@ -843,7 +844,7 @@
scheme.equals(ContentResolver.SCHEME_FILE)) {
path = uri.getPath();
- } else if (scheme.equals("http")) {
+ } else if (scheme.equals("http") || scheme.equals("https")) {
path = uri.toString();
} else if (scheme.equals(ContentResolver.SCHEME_CONTENT)) {
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index b33cfe2..0c515a4 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -81,7 +81,7 @@
explicit Matrix4(const float* v) { load(v); }
- Matrix4(const SkMatrix& v) { // NOLINT, implicit
+ Matrix4(const SkMatrix& v) { // NOLINT(google-explicit-constructor)
load(v);
}
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index d6362ef..24443c8 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -57,15 +57,15 @@
inline Rect(float width, float height) : left(0.0f), top(0.0f), right(width), bottom(height) {}
- inline Rect(const SkIRect& rect)
- : // NOLINT, implicit
+ inline Rect(const SkIRect& rect) // NOLINT(google-explicit-constructor)
+ :
left(rect.fLeft)
, top(rect.fTop)
, right(rect.fRight)
, bottom(rect.fBottom) {}
- inline Rect(const SkRect& rect)
- : // NOLINT, implicit
+ inline Rect(const SkRect& rect) // NOLINT(google-explicit-constructor)
+ :
left(rect.fLeft)
, top(rect.fTop)
, right(rect.fRight)
diff --git a/libs/hwui/debug/GlesErrorCheckWrapper.h b/libs/hwui/debug/GlesErrorCheckWrapper.h
index ee5cc1f..791400b 100644
--- a/libs/hwui/debug/GlesErrorCheckWrapper.h
+++ b/libs/hwui/debug/GlesErrorCheckWrapper.h
@@ -24,7 +24,7 @@
class GlesErrorCheckWrapper : public GlesDriver {
public:
- GlesErrorCheckWrapper(GlesDriver& base) : mBase(base) {}
+ explicit GlesErrorCheckWrapper(GlesDriver& base) : mBase(base) {}
#define GL_ENTRY(ret, api, ...) virtual ret api##_(__VA_ARGS__) override;
#include "gles_decls.in"
diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h
index c1a3b6d..92ffda9 100644
--- a/libs/hwui/hwui/Paint.h
+++ b/libs/hwui/hwui/Paint.h
@@ -46,7 +46,7 @@
Paint();
Paint(const Paint& paint);
- Paint(const SkPaint& paint); // NOLINT(implicit)
+ Paint(const SkPaint& paint); // NOLINT(google-explicit-constructor)
~Paint();
Paint& operator=(const Paint& other);
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
index a5faae7..c3563db 100644
--- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
@@ -36,8 +36,6 @@
namespace uirenderer {
namespace skiapipeline {
-static std::mutex sLock{};
-static ThreadBase* sGLDrawThread = nullptr;
static renderthread::EglManager sEglManager;
// ScopedDrawRequest makes sure a GL thread is started and EGL context is initialized on it.
@@ -47,32 +45,20 @@
private:
void beginDraw() {
- std::lock_guard _lock{sLock};
-
- if (!sGLDrawThread) {
- sGLDrawThread = new ThreadBase{};
- }
-
- if (!sGLDrawThread->isRunning()) {
- sGLDrawThread->start("GLFunctorThread");
- }
-
if (!sEglManager.hasEglContext()) {
- sGLDrawThread->queue().runSync([]() { sEglManager.initialize(); });
+ sEglManager.initialize();
}
}
};
void VkInteropFunctorDrawable::vkInvokeFunctor(Functor* functor) {
ScopedDrawRequest _drawRequest{};
- sGLDrawThread->queue().runSync([&]() {
- EGLDisplay display = sEglManager.eglDisplay();
- DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
- if (display != EGL_NO_DISPLAY) {
- mode = DrawGlInfo::kModeProcess;
- }
- (*functor)(mode, nullptr);
- });
+ EGLDisplay display = sEglManager.eglDisplay();
+ DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
+ if (display != EGL_NO_DISPLAY) {
+ mode = DrawGlInfo::kModeProcess;
+ }
+ (*functor)(mode, nullptr);
}
#define FENCE_TIMEOUT 2000000000
@@ -113,7 +99,7 @@
// TODO: draw command has completed.
// TODO: A simple but inefficient way is to flush and issue a QueueWaitIdle call. See
// TODO: GrVkGpu::destroyResources() for example.
- bool success = sGLDrawThread->queue().runSync([&]() -> bool {
+ {
ATRACE_FORMAT("WebViewDraw_%dx%d", mFBInfo.width(), mFBInfo.height());
EGLDisplay display = sEglManager.eglDisplay();
LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY, "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
@@ -125,7 +111,7 @@
if (autoImage.image == EGL_NO_IMAGE_KHR) {
ALOGW("Could not create EGL image, err =%s",
uirenderer::renderthread::EglManager::eglErrorString());
- return false;
+ return;
}
AutoSkiaGlTexture glTexture;
@@ -156,7 +142,7 @@
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
ALOGE("Failed framebuffer check for created target buffer: %s",
GLUtils::getGLFramebufferError());
- return false;
+ return;
}
glDisable(GL_STENCIL_TEST);
@@ -183,11 +169,6 @@
LOG_ALWAYS_FATAL_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR,
"Failed to wait for the fence %#x", eglGetError());
eglDestroySyncKHR(display, glDrawFinishedFence);
- return true;
- });
-
- if (!success) {
- return;
}
SkPaint paint;
@@ -208,15 +189,14 @@
if (auto lp = std::get_if<LegacyFunctor>(&mAnyFunctor)) {
if (lp->listener) {
ScopedDrawRequest _drawRequest{};
- sGLDrawThread->queue().runSync(
- [&]() { lp->listener->onGlFunctorReleased(lp->functor); });
+ lp->listener->onGlFunctorReleased(lp->functor);
}
}
}
void VkInteropFunctorDrawable::syncFunctor(const WebViewSyncData& data) const {
ScopedDrawRequest _drawRequest{};
- sGLDrawThread->queue().runSync([&]() { FunctorDrawable::syncFunctor(data); });
+ FunctorDrawable::syncFunctor(data);
}
} // namespace skiapipeline
diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp
index 29e4256..8a16b20 100644
--- a/libs/hwui/service/GraphicsStatsService.cpp
+++ b/libs/hwui/service/GraphicsStatsService.cpp
@@ -48,7 +48,7 @@
class FileDescriptor {
public:
- FileDescriptor(int fd) : mFd(fd) {}
+ explicit FileDescriptor(int fd) : mFd(fd) {}
~FileDescriptor() {
if (mFd != -1) {
close(mFd);
@@ -56,7 +56,7 @@
}
}
bool valid() { return mFd != -1; }
- operator int() { return mFd; }
+ operator int() { return mFd; } // NOLINT(google-explicit-constructor)
private:
int mFd;
@@ -64,7 +64,7 @@
class FileOutputStreamLite : public io::ZeroCopyOutputStream {
public:
- FileOutputStreamLite(int fd) : mCopyAdapter(fd), mImpl(&mCopyAdapter) {}
+ explicit FileOutputStreamLite(int fd) : mCopyAdapter(fd), mImpl(&mCopyAdapter) {}
virtual ~FileOutputStreamLite() {}
int GetErrno() { return mCopyAdapter.mErrno; }
@@ -82,7 +82,7 @@
int mFd;
int mErrno = 0;
- FDAdapter(int fd) : mFd(fd) {}
+ explicit FDAdapter(int fd) : mFd(fd) {}
virtual ~FDAdapter() {}
virtual bool Write(const void* buffer, int size) override {
diff --git a/libs/hwui/utils/LinearAllocator.h b/libs/hwui/utils/LinearAllocator.h
index b401fcf..9c4a1be 100644
--- a/libs/hwui/utils/LinearAllocator.h
+++ b/libs/hwui/utils/LinearAllocator.h
@@ -168,7 +168,7 @@
};
// enable allocators to be constructed from other templated types
template <class U>
- LinearStdAllocator(const LinearStdAllocator<U>& other) // NOLINT(implicit)
+ LinearStdAllocator(const LinearStdAllocator<U>& other) // NOLINT(google-explicit-constructor)
: linearAllocator(other.linearAllocator) {}
T* allocate(size_t num, const void* = 0) {
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 0a90f85..b9b1cdc 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -89,10 +89,6 @@
mLocked.animationPending = false;
- mLocked.displayWidth = -1;
- mLocked.displayHeight = -1;
- mLocked.displayOrientation = DISPLAY_ORIENTATION_0;
-
mLocked.presentation = PRESENTATION_POINTER;
mLocked.presentationChanged = false;
@@ -110,15 +106,6 @@
mLocked.lastFrameUpdatedTime = 0;
mLocked.buttonState = 0;
-
- mPolicy->loadPointerIcon(&mLocked.pointerIcon);
-
- loadResources();
-
- if (mLocked.pointerIcon.isValid()) {
- mLocked.pointerIconChanged = true;
- updatePointerLocked();
- }
}
PointerController::~PointerController() {
@@ -144,23 +131,15 @@
bool PointerController::getBoundsLocked(float* outMinX, float* outMinY,
float* outMaxX, float* outMaxY) const {
- if (mLocked.displayWidth <= 0 || mLocked.displayHeight <= 0) {
+
+ if (!mLocked.viewport.isValid()) {
return false;
}
- *outMinX = 0;
- *outMinY = 0;
- switch (mLocked.displayOrientation) {
- case DISPLAY_ORIENTATION_90:
- case DISPLAY_ORIENTATION_270:
- *outMaxX = mLocked.displayHeight - 1;
- *outMaxY = mLocked.displayWidth - 1;
- break;
- default:
- *outMaxX = mLocked.displayWidth - 1;
- *outMaxY = mLocked.displayHeight - 1;
- break;
- }
+ *outMinX = mLocked.viewport.logicalLeft;
+ *outMinY = mLocked.viewport.logicalTop;
+ *outMaxX = mLocked.viewport.logicalRight - 1;
+ *outMaxY = mLocked.viewport.logicalBottom - 1;
return true;
}
@@ -231,6 +210,12 @@
*outY = mLocked.pointerY;
}
+int32_t PointerController::getDisplayId() const {
+ AutoMutex _l(mLock);
+
+ return mLocked.viewport.displayId;
+}
+
void PointerController::fade(Transition transition) {
AutoMutex _l(mLock);
@@ -355,48 +340,56 @@
void PointerController::reloadPointerResources() {
AutoMutex _l(mLock);
- loadResources();
-
- if (mLocked.presentation == PRESENTATION_POINTER) {
- mLocked.additionalMouseResources.clear();
- mLocked.animationResources.clear();
- mPolicy->loadPointerIcon(&mLocked.pointerIcon);
- mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
- &mLocked.animationResources);
- }
-
- mLocked.presentationChanged = true;
+ loadResourcesLocked();
updatePointerLocked();
}
-void PointerController::setDisplayViewport(int32_t width, int32_t height, int32_t orientation) {
- AutoMutex _l(mLock);
+/**
+ * The viewport values for deviceHeight and deviceWidth have already been adjusted for rotation,
+ * so here we are getting the dimensions in the original, unrotated orientation (orientation 0).
+ */
+static void getNonRotatedSize(const DisplayViewport& viewport, int32_t& width, int32_t& height) {
+ width = viewport.deviceWidth;
+ height = viewport.deviceHeight;
- // Adjust to use the display's unrotated coordinate frame.
- if (orientation == DISPLAY_ORIENTATION_90
- || orientation == DISPLAY_ORIENTATION_270) {
- int32_t temp = height;
- height = width;
- width = temp;
+ if (viewport.orientation == DISPLAY_ORIENTATION_90
+ || viewport.orientation == DISPLAY_ORIENTATION_270) {
+ std::swap(width, height);
+ }
+}
+
+void PointerController::setDisplayViewport(const DisplayViewport& viewport) {
+ AutoMutex _l(mLock);
+ if (viewport == mLocked.viewport) {
+ return;
}
- if (mLocked.displayWidth != width || mLocked.displayHeight != height) {
- mLocked.displayWidth = width;
- mLocked.displayHeight = height;
+ const DisplayViewport oldViewport = mLocked.viewport;
+ mLocked.viewport = viewport;
+
+ int32_t oldDisplayWidth, oldDisplayHeight;
+ getNonRotatedSize(oldViewport, oldDisplayWidth, oldDisplayHeight);
+ int32_t newDisplayWidth, newDisplayHeight;
+ getNonRotatedSize(viewport, newDisplayWidth, newDisplayHeight);
+
+ // Reset cursor position to center if size or display changed.
+ if (oldViewport.displayId != viewport.displayId
+ || oldDisplayWidth != newDisplayWidth
+ || oldDisplayHeight != newDisplayHeight) {
float minX, minY, maxX, maxY;
if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
mLocked.pointerX = (minX + maxX) * 0.5f;
mLocked.pointerY = (minY + maxY) * 0.5f;
+ // Reload icon resources for density may be changed.
+ loadResourcesLocked();
} else {
mLocked.pointerX = 0;
mLocked.pointerY = 0;
}
fadeOutAndReleaseAllSpotsLocked();
- }
-
- if (mLocked.displayOrientation != orientation) {
+ } else if (oldViewport.orientation != viewport.orientation) {
// Apply offsets to convert from the pixel top-left corner position to the pixel center.
// This creates an invariant frame of reference that we can easily rotate when
// taking into account that the pointer may be located at fractional pixel offsets.
@@ -405,37 +398,37 @@
float temp;
// Undo the previous rotation.
- switch (mLocked.displayOrientation) {
+ switch (oldViewport.orientation) {
case DISPLAY_ORIENTATION_90:
temp = x;
- x = mLocked.displayWidth - y;
+ x = oldViewport.deviceHeight - y;
y = temp;
break;
case DISPLAY_ORIENTATION_180:
- x = mLocked.displayWidth - x;
- y = mLocked.displayHeight - y;
+ x = oldViewport.deviceWidth - x;
+ y = oldViewport.deviceHeight - y;
break;
case DISPLAY_ORIENTATION_270:
temp = x;
x = y;
- y = mLocked.displayHeight - temp;
+ y = oldViewport.deviceWidth - temp;
break;
}
// Perform the new rotation.
- switch (orientation) {
+ switch (viewport.orientation) {
case DISPLAY_ORIENTATION_90:
temp = x;
x = y;
- y = mLocked.displayWidth - temp;
+ y = viewport.deviceHeight - temp;
break;
case DISPLAY_ORIENTATION_180:
- x = mLocked.displayWidth - x;
- y = mLocked.displayHeight - y;
+ x = viewport.deviceWidth - x;
+ y = viewport.deviceHeight - y;
break;
case DISPLAY_ORIENTATION_270:
temp = x;
- x = mLocked.displayHeight - y;
+ x = viewport.deviceWidth - y;
y = temp;
break;
}
@@ -444,7 +437,6 @@
// and save the results.
mLocked.pointerX = x - 0.5f;
mLocked.pointerY = y - 0.5f;
- mLocked.displayOrientation = orientation;
}
updatePointerLocked();
@@ -614,11 +606,16 @@
mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT);
}
-void PointerController::updatePointerLocked() {
+void PointerController::updatePointerLocked() REQUIRES(mLock) {
+ if (!mLocked.viewport.isValid()) {
+ return;
+ }
+
mSpriteController->openTransaction();
mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);
mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY);
+ mLocked.pointerSprite->setDisplayId(mLocked.viewport.displayId);
if (mLocked.pointerAlpha > 0) {
mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha);
@@ -729,8 +726,18 @@
}
}
-void PointerController::loadResources() {
+void PointerController::loadResourcesLocked() REQUIRES(mLock) {
mPolicy->loadPointerResources(&mResources);
+
+ if (mLocked.presentation == PRESENTATION_POINTER) {
+ mLocked.additionalMouseResources.clear();
+ mLocked.animationResources.clear();
+ mPolicy->loadPointerIcon(&mLocked.pointerIcon);
+ mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
+ &mLocked.animationResources);
+ }
+
+ mLocked.pointerIconChanged = true;
}
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 7f4e5a5..a32cc42 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -23,6 +23,7 @@
#include <vector>
#include <ui/DisplayInfo.h>
+#include <input/DisplayViewport.h>
#include <input/Input.h>
#include <PointerControllerInterface.h>
#include <utils/BitSet.h>
@@ -96,6 +97,7 @@
virtual int32_t getButtonState() const;
virtual void setPosition(float x, float y);
virtual void getPosition(float* outX, float* outY) const;
+ virtual int32_t getDisplayId() const;
virtual void fade(Transition transition);
virtual void unfade(Transition transition);
@@ -106,7 +108,7 @@
void updatePointerIcon(int32_t iconId);
void setCustomPointerIcon(const SpriteIcon& icon);
- void setDisplayViewport(int32_t width, int32_t height, int32_t orientation);
+ void setDisplayViewport(const DisplayViewport& viewport);
void setInactivityTimeout(InactivityTimeout inactivityTimeout);
void reloadPointerResources();
@@ -156,9 +158,7 @@
size_t animationFrameIndex;
nsecs_t lastFrameUpdatedTime;
- int32_t displayWidth;
- int32_t displayHeight;
- int32_t displayOrientation;
+ DisplayViewport viewport;
InactivityTimeout inactivityTimeout;
@@ -182,7 +182,7 @@
Vector<Spot*> spots;
Vector<sp<Sprite> > recycledSprites;
- } mLocked;
+ } mLocked GUARDED_BY(mLock);
bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
void setPositionLocked(float x, float y);
@@ -207,7 +207,7 @@
void fadeOutAndReleaseSpotLocked(Spot* spot);
void fadeOutAndReleaseAllSpotsLocked();
- void loadResources();
+ void loadResourcesLocked();
};
} // namespace android
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index eb2bc98..c1868d3 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -144,13 +144,16 @@
}
}
- // Resize sprites if needed.
+ // Resize and/or reparent sprites if needed.
SurfaceComposerClient::Transaction t;
bool needApplyTransaction = false;
for (size_t i = 0; i < numSprites; i++) {
SpriteUpdate& update = updates.editItemAt(i);
+ if (update.state.surfaceControl == nullptr) {
+ continue;
+ }
- if (update.state.surfaceControl != NULL && update.state.wantSurfaceVisible()) {
+ if (update.state.wantSurfaceVisible()) {
int32_t desiredWidth = update.state.icon.bitmap.width();
int32_t desiredHeight = update.state.icon.bitmap.height();
if (update.state.surfaceWidth < desiredWidth
@@ -170,6 +173,12 @@
}
}
}
+
+ // If surface is a new one, we have to set right layer stack.
+ if (update.surfaceChanged || update.state.dirty & DIRTY_DISPLAY_ID) {
+ t.setLayerStack(update.state.surfaceControl, update.state.displayId);
+ needApplyTransaction = true;
+ }
}
if (needApplyTransaction) {
t.apply();
@@ -236,7 +245,7 @@
if (update.state.surfaceControl != NULL && (becomingVisible || becomingHidden
|| (wantSurfaceVisibleAndDrawn && (update.state.dirty & (DIRTY_ALPHA
| DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER
- | DIRTY_VISIBILITY | DIRTY_HOTSPOT))))) {
+ | DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID))))) {
needApplyTransaction = true;
if (wantSurfaceVisibleAndDrawn
@@ -445,6 +454,15 @@
}
}
+void SpriteController::SpriteImpl::setDisplayId(int32_t displayId) {
+ AutoMutex _l(mController->mLock);
+
+ if (mLocked.state.displayId != displayId) {
+ mLocked.state.displayId = displayId;
+ invalidateLocked(DIRTY_DISPLAY_ID);
+ }
+}
+
void SpriteController::SpriteImpl::invalidateLocked(uint32_t dirty) {
bool wasDirty = mLocked.state.dirty;
mLocked.state.dirty |= dirty;
diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h
index 31e43e9..5b216f5 100644
--- a/libs/input/SpriteController.h
+++ b/libs/input/SpriteController.h
@@ -125,6 +125,9 @@
/* Sets the sprite transformation matrix. */
virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix) = 0;
+
+ /* Sets the id of the display where the sprite should be shown. */
+ virtual void setDisplayId(int32_t displayId) = 0;
};
/*
@@ -170,6 +173,7 @@
DIRTY_LAYER = 1 << 4,
DIRTY_VISIBILITY = 1 << 5,
DIRTY_HOTSPOT = 1 << 6,
+ DIRTY_DISPLAY_ID = 1 << 7,
};
/* Describes the state of a sprite.
@@ -180,7 +184,7 @@
struct SpriteState {
inline SpriteState() :
dirty(0), visible(false),
- positionX(0), positionY(0), layer(0), alpha(1.0f),
+ positionX(0), positionY(0), layer(0), alpha(1.0f), displayId(ADISPLAY_ID_DEFAULT),
surfaceWidth(0), surfaceHeight(0), surfaceDrawn(false), surfaceVisible(false) {
}
@@ -193,6 +197,7 @@
int32_t layer;
float alpha;
SpriteTransformationMatrix transformationMatrix;
+ int32_t displayId;
sp<SurfaceControl> surfaceControl;
int32_t surfaceWidth;
@@ -225,6 +230,7 @@
virtual void setLayer(int32_t layer);
virtual void setAlpha(float alpha);
virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix);
+ virtual void setDisplayId(int32_t displayId);
inline const SpriteState& getStateLocked() const {
return mLocked.state;
diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
index b531325..866634e 100644
--- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -16,9 +16,6 @@
package com.android.internal.location;
-import java.io.UnsupportedEncodingException;
-import java.util.concurrent.TimeUnit;
-
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -26,20 +23,23 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.location.LocationManager;
import android.location.INetInitiatedListener;
+import android.location.LocationManager;
+import android.os.RemoteException;
import android.os.SystemClock;
-import android.telephony.TelephonyManager;
+import android.os.UserHandle;
import android.telephony.PhoneNumberUtils;
import android.telephony.PhoneStateListener;
-import android.os.RemoteException;
-import android.os.UserHandle;
+import android.telephony.TelephonyManager;
import android.util.Log;
-import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.R;
+import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.telephony.GsmAlphabet;
+import java.io.UnsupportedEncodingException;
+import java.util.concurrent.TimeUnit;
+
/**
* A GPS Network-initiated Handler class used by LocationManager.
*
@@ -92,9 +92,6 @@
public static final int GPS_ENC_SUPL_UCS2 = 3;
public static final int GPS_ENC_UNKNOWN = -1;
- // Limit on SUPL NI emergency mode time extension after emergency sessions ends
- private static final int MAX_EMERGENCY_MODE_EXTENSION_SECONDS = 300; // 5 minute maximum
-
private final Context mContext;
private final TelephonyManager mTelephonyManager;
private final PhoneStateListener mPhoneStateListener;
@@ -252,19 +249,9 @@
}
public void setEmergencyExtensionSeconds(int emergencyExtensionSeconds) {
- if (emergencyExtensionSeconds > MAX_EMERGENCY_MODE_EXTENSION_SECONDS) {
- Log.w(TAG, "emergencyExtensionSeconds " + emergencyExtensionSeconds
- + " too high, reset to " + MAX_EMERGENCY_MODE_EXTENSION_SECONDS);
- emergencyExtensionSeconds = MAX_EMERGENCY_MODE_EXTENSION_SECONDS;
- } else if (emergencyExtensionSeconds < 0) {
- Log.w(TAG, "emergencyExtensionSeconds " + emergencyExtensionSeconds
- + " is negative, reset to zero.");
- emergencyExtensionSeconds = 0;
- }
mEmergencyExtensionMillis = TimeUnit.SECONDS.toMillis(emergencyExtensionSeconds);
}
-
// Handles NI events from HAL
public void handleNiNotification(GpsNiNotification notif) {
if (DEBUG) Log.d(TAG, "in handleNiNotification () :"
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index f482f64..df96994 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -1334,11 +1334,11 @@
return addTask(new Task(CALL_COMPLETED_SET_WAKE_LOCK, false) {
@Override
void process() {
- boolean washeld = false;
+ boolean wasHeld = false;
if (mWakeLock != null) {
if (mWakeLock.isHeld()) {
- washeld = true;
+ wasHeld = true;
mWakeLock.release();
}
}
@@ -1346,7 +1346,7 @@
mWakeLock = wakeLock;
if (mWakeLock != null) {
mWakeLock.setReferenceCounted(false);
- if (washeld) {
+ if (wasHeld) {
mWakeLock.acquire();
}
}
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index eeb1461..25b1df2 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -702,6 +702,12 @@
* is no guarantee that the recorder will have stopped by the time the
* listener is notified.
*
+ * <p>When using MPEG-4 container ({@link #setOutputFormat(int)} with
+ * {@link OutputFormat#MPEG_4}), it is recommended to set maximum duration that fits the use
+ * case. Setting a larger than required duration may result in a larger than needed output file
+ * because of space reserved for MOOV box expecting large movie data in this recording session.
+ * Unused space of MOOV box is turned into FREE box in the output file.</p>
+ *
* @param max_duration_ms the maximum duration in ms (if zero or negative, disables the duration limit)
*
*/
@@ -717,6 +723,12 @@
* is no guarantee that the recorder will have stopped by the time the
* listener is notified.
*
+ * <p>When using MPEG-4 container ({@link #setOutputFormat(int)} with
+ * {@link OutputFormat#MPEG_4}), it is recommended to set maximum filesize that fits the use
+ * case. Setting a larger than required filesize may result in a larger than needed output file
+ * because of space reserved for MOOV box expecting large movie data in this recording session.
+ * Unused space of MOOV box is turned into FREE box in the output file.</p>
+ *
* @param max_filesize_bytes the maximum filesize in bytes (if zero or negative, disables the limit)
*
*/
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index fda0fc4..d6b6339 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -102,7 +102,6 @@
"libhidlbase",
"libhidlmemory",
- "libpowermanager", // Used by JWakeLock. Will be replace with public SDK API.
"libmediametrics", // Used by MediaMetrics. Will be replaced with stable C API.
"libbinder", // Used by JWakeLock and MediaMetrics.
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 7e6a8ab..9b4e730 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -86,7 +86,8 @@
// ----------------------------------------------------------------------------
struct fields_t {
- jfieldID context;
+ jfieldID context; // passed from Java to native, used for creating JWakeLock
+ jfieldID nativeContext; // mNativeContext in MediaPlayer2.java
jfieldID surface_texture;
jmethodID post_event;
@@ -225,21 +226,21 @@
static sp<MediaPlayer2> getMediaPlayer(JNIEnv* env, jobject thiz)
{
Mutex::Autolock l(sLock);
- MediaPlayer2* const p = (MediaPlayer2*)env->GetLongField(thiz, fields.context);
+ MediaPlayer2* const p = (MediaPlayer2*)env->GetLongField(thiz, fields.nativeContext);
return sp<MediaPlayer2>(p);
}
static sp<MediaPlayer2> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer2>& player)
{
Mutex::Autolock l(sLock);
- sp<MediaPlayer2> old = (MediaPlayer2*)env->GetLongField(thiz, fields.context);
+ sp<MediaPlayer2> old = (MediaPlayer2*)env->GetLongField(thiz, fields.nativeContext);
if (player.get()) {
player->incStrong((void*)setMediaPlayer);
}
if (old != 0) {
old->decStrong((void*)setMediaPlayer);
}
- env->SetLongField(thiz, fields.context, (jlong)player.get());
+ env->SetLongField(thiz, fields.nativeContext, (jlong)player.get());
return old;
}
@@ -955,11 +956,16 @@
return;
}
- fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
+ fields.context = env->GetFieldID(clazz, "mContext", "Landroid/content/Context;");
if (fields.context == NULL) {
return;
}
+ fields.nativeContext = env->GetFieldID(clazz, "mNativeContext", "J");
+ if (fields.nativeContext == NULL) {
+ return;
+ }
+
fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
"(Ljava/lang/Object;JIII[B)V");
if (fields.post_event == NULL) {
@@ -1013,7 +1019,8 @@
jint sessionId, jobject weak_this)
{
ALOGV("native_setup");
- sp<MediaPlayer2> mp = MediaPlayer2::Create(sessionId);
+ jobject context = env->GetObjectField(thiz, fields.context);
+ sp<MediaPlayer2> mp = MediaPlayer2::Create(sessionId, context);
if (mp == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
return;
diff --git a/native/webview/plat_support/draw_functor.cpp b/native/webview/plat_support/draw_functor.cpp
index 820bac5..6c1ceab 100644
--- a/native/webview/plat_support/draw_functor.cpp
+++ b/native/webview/plat_support/draw_functor.cpp
@@ -82,13 +82,9 @@
.onDestroyed = &onDestroyed,
};
if (!callbacks_initialized) {
- switch (uirenderer::WebViewFunctor_queryPlatformRenderMode()) {
- case uirenderer::RenderMode::OpenGL_ES:
- webview_functor_callbacks.gles.draw = &draw_gl;
- break;
- case uirenderer::RenderMode::Vulkan:
- break;
- }
+ // Under uirenderer::RenderMode::Vulkan, whether gles or vk union should
+ // be populated should match whether the vk-gl interop is used.
+ webview_functor_callbacks.gles.draw = &draw_gl;
callbacks_initialized = true;
}
SupportData* support = new SupportData{
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index af52c00..fab1bcc 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -366,9 +366,9 @@
}
@Override
- public void onNotificationDirectReply(@NonNull String key) {
- if (DEBUG) Log.i(TAG, "onNotificationDirectReply " + key);
- mSmartActionsHelper.onNotificationDirectReply(key);
+ public void onNotificationDirectReplied(@NonNull String key) {
+ if (DEBUG) Log.i(TAG, "onNotificationDirectReplied " + key);
+ mSmartActionsHelper.onNotificationDirectReplied(key);
}
@Override
diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
index f2db2da..a9d8f62 100644
--- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
+++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
@@ -165,7 +165,7 @@
}
}
- void onNotificationDirectReply(@NonNull String key) {
+ void onNotificationDirectReplied(@NonNull String key) {
if (mTextClassifier == null) {
return;
}
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java
index da382a0..7d74788 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java
@@ -241,7 +241,7 @@
when(mNotificationEntry.getNotification()).thenReturn(notification);
mSmartActionsHelper.suggestReplies(mNotificationEntry);
- mSmartActionsHelper.onNotificationDirectReply(NOTIFICATION_KEY);
+ mSmartActionsHelper.onNotificationDirectReplied(NOTIFICATION_KEY);
ArgumentCaptor<TextClassifierEvent> argumentCaptor =
ArgumentCaptor.forClass(TextClassifierEvent.class);
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 7dcc3ac..03c6205 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1137,9 +1137,6 @@
<!-- The notice header of Third-party licenses. not translatable -->
<string name="notice_header" translatable="false"></string>
- <!-- UI debug setting: opt in to use updated graphics driver? [CHAR LIMIT=100] -->
- <string name="gup_dev_opt_in_app_summary">Opt in app to use Game Update Package in developement</string>
-
<!-- Name of the phone device [CHAR LIMIT=NONE] -->
<string name="media_transfer_phone_device_name">Phone speaker</string>
</resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 526efcb..419273e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -708,6 +708,9 @@
Settings.Global.GUP_DEV_OPT_IN_APPS,
GlobalSettingsProto.Gpu.GUP_DEV_OPT_IN_APPS);
dumpSetting(s, p,
+ Settings.Global.GUP_DEV_OPT_OUT_APPS,
+ GlobalSettingsProto.Gpu.GUP_DEV_OPT_OUT_APPS);
+ dumpSetting(s, p,
Settings.Global.GUP_BLACK_LIST,
GlobalSettingsProto.Gpu.GUP_BLACK_LIST);
p.end(gpuToken);
diff --git a/packages/SystemUI/res/drawable/bubble_expanded_header_bg.xml b/packages/SystemUI/res/drawable/bubble_expanded_header_bg.xml
new file mode 100644
index 0000000..26bf981
--- /dev/null
+++ b/packages/SystemUI/res/drawable/bubble_expanded_header_bg.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ 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
+ -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item>
+ <shape android:shape="rectangle">
+ <solid android:color="?android:attr/colorBackgroundFloating"/>
+ <corners
+ android:topLeftRadius="@dimen/corner_size"
+ android:topRightRadius="@dimen/corner_size"/>
+ </shape>
+ </item>
+ <item android:gravity="bottom">
+ <shape>
+ <size android:height="1dp"/>
+ <solid android:color="?android:attr/textColorSecondary" />
+ </shape>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/battery_percentage_view.xml b/packages/SystemUI/res/layout/battery_percentage_view.xml
index e52aa14..b9b1bb1 100644
--- a/packages/SystemUI/res/layout/battery_percentage_view.xml
+++ b/packages/SystemUI/res/layout/battery_percentage_view.xml
@@ -22,7 +22,7 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:singleLine="true"
- android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
+ android:textAppearance="@style/TextAppearance.StatusBar.Clock"
android:textColor="?android:attr/textColorPrimary"
android:gravity="center_vertical|start"
android:paddingStart="@dimen/battery_level_padding_start"
diff --git a/packages/SystemUI/res/layout/bubble_expanded_view.xml b/packages/SystemUI/res/layout/bubble_expanded_view.xml
index b2307e7..1aeb52c 100644
--- a/packages/SystemUI/res/layout/bubble_expanded_view.xml
+++ b/packages/SystemUI/res/layout/bubble_expanded_view.xml
@@ -20,11 +20,23 @@
android:layout_width="match_parent"
android:id="@+id/bubble_expanded_view">
- <!-- TODO: header -->
-
<View
android:id="@+id/pointer_view"
android:layout_width="@dimen/bubble_pointer_width"
android:layout_height="@dimen/bubble_pointer_height"
/>
+
+ <TextView
+ android:id="@+id/bubble_content_header"
+ android:background="@drawable/bubble_expanded_header_bg"
+ android:textAppearance="@*android:style/TextAppearance.Material.Title"
+ android:textSize="18sp"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/bubble_expanded_header_height"
+ android:gravity="start|center_vertical"
+ android:singleLine="true"
+ android:paddingLeft="@dimen/bubble_expanded_header_horizontal_padding"
+ android:paddingRight="@dimen/bubble_expanded_header_horizontal_padding"
+ />
+
</com.android.systemui.bubbles.BubbleExpandedViewContainer>
diff --git a/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml b/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml
index 307b538..5bcc1b3 100644
--- a/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml
+++ b/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml
@@ -39,7 +39,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/monitoring_title_device_owned"
- style="@android:style/TextAppearance.Material.Title"
+ style="@style/TextAppearance.DeviceManagementDialog.Title"
android:textColor="?android:attr/textColorPrimary"
android:paddingBottom="@dimen/qs_footer_dialog_subtitle_padding"
/>
@@ -64,7 +64,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/monitoring_subtitle_ca_certificate"
- style="@android:style/TextAppearance.Material.Title"
+ style="@style/TextAppearance.DeviceManagementDialog.Title"
android:textColor="?android:attr/textColorPrimary"
android:paddingBottom="@dimen/qs_footer_dialog_subtitle_padding"
/>
@@ -89,7 +89,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/monitoring_subtitle_network_logging"
- style="@android:style/TextAppearance.Material.Title"
+ style="@style/TextAppearance.DeviceManagementDialog.Title"
android:textColor="?android:attr/textColorPrimary"
android:paddingBottom="@dimen/qs_footer_dialog_subtitle_padding"
/>
@@ -114,7 +114,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/monitoring_subtitle_vpn"
- style="@android:style/TextAppearance.Material.Title"
+ style="@style/TextAppearance.DeviceManagementDialog.Title"
android:textColor="?android:attr/textColorPrimary"
android:paddingBottom="@dimen/qs_footer_dialog_subtitle_padding"
/>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 889db66..7cc5524 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -449,6 +449,11 @@
better (narrower) line-break for a double-line smart reply button. -->
<integer name="config_smart_replies_in_notifications_max_squeeze_remeasure_attempts">3</integer>
+ <!-- Smart replies in notifications: Whether by default tapping on a choice should let the user
+ edit the input before it is sent to the app. Developers can override this via
+ RemoteInput.Builder.setEditChoicesBeforeSending. -->
+ <bool name="config_smart_replies_in_notifications_edit_choices_before_sending">false</bool>
+
<!-- Screenshot editing default activity. Must handle ACTION_EDIT image/png intents.
Blank sends the user to the Chooser first.
This name is in the ComponentName flattened format (package/class) -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 06df0e7..10e5f74 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -997,4 +997,8 @@
<dimen name="bubble_pointer_width">6dp</dimen>
<!-- Extra padding around the dismiss target for bubbles -->
<dimen name="bubble_dismiss_slop">16dp</dimen>
+ <!-- Height of the header within the expanded view. -->
+ <dimen name="bubble_expanded_header_height">48dp</dimen>
+ <!-- Left and right padding applied to the header. -->
+ <dimen name="bubble_expanded_header_horizontal_padding">24dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 8a5a69b..22a0b36 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -125,7 +125,7 @@
<style name="TextAppearance.StatusBar.Clock" parent="@*android:style/TextAppearance.StatusBar.Icon">
<item name="android:textSize">@dimen/status_bar_clock_size</item>
- <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
<item name="android:textColor">@color/status_bar_clock_color</item>
</style>
@@ -265,6 +265,10 @@
<item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
</style>
+ <style name="TextAppearance.DeviceManagementDialog.Title" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle">
+ <item name="android:gravity">center</item>
+ </style>
+
<style name="BaseBrightnessDialogContainer" parent="@style/Theme.SystemUI">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java
index 9f363f6..7e5b426 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java
@@ -25,12 +25,20 @@
private int mUid;
private String mPackageName;
private long mTimeStarted;
+ private String mState;
public AppOpItem(int code, int uid, String packageName, long timeStarted) {
this.mCode = code;
this.mUid = uid;
this.mPackageName = packageName;
this.mTimeStarted = timeStarted;
+ mState = new StringBuilder()
+ .append("AppOpItem(")
+ .append("Op code=").append(code).append(", ")
+ .append("UID=").append(uid).append(", ")
+ .append("Package name=").append(packageName)
+ .append(")")
+ .toString();
}
public int getCode() {
@@ -48,4 +56,9 @@
public long getTimeStarted() {
return mTimeStarted;
}
+
+ @Override
+ public String toString() {
+ return mState;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index e3bcb37..c013df3 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -29,7 +29,10 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dumpable;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -47,7 +50,7 @@
@Singleton
public class AppOpsControllerImpl implements AppOpsController,
AppOpsManager.OnOpActiveChangedListener,
- AppOpsManager.OnOpNotedListener {
+ AppOpsManager.OnOpNotedListener, Dumpable {
private static final long NOTED_OP_TIME_DELAY_MS = 5000;
private static final String TAG = "AppOpsControllerImpl";
@@ -271,6 +274,22 @@
}
}
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("AppOpsController state:");
+ pw.println(" Active Items:");
+ for (int i = 0; i < mActiveItems.size(); i++) {
+ final AppOpItem item = mActiveItems.get(i);
+ pw.print(" "); pw.println(item.toString());
+ }
+ pw.println(" Noted Items:");
+ for (int i = 0; i < mNotedItems.size(); i++) {
+ final AppOpItem item = mNotedItems.get(i);
+ pw.print(" "); pw.println(item.toString());
+ }
+
+ }
+
protected final class H extends Handler {
H(Looper looper) {
super(looper);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 7577609..9f3ff78 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -23,15 +23,20 @@
import static com.android.systemui.bubbles.BubbleMovementHelper.EDGE_OVERLAP;
import android.app.Notification;
+import android.app.PendingIntent;
import android.content.Context;
+import android.content.pm.ActivityInfo;
import android.graphics.Point;
import android.graphics.Rect;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
+import android.util.Log;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.FrameLayout;
+import androidx.annotation.Nullable;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dependency;
import com.android.systemui.R;
@@ -68,6 +73,8 @@
private static final String ENABLE_AUTO_BUBBLE_MESSAGES = "experiment_autobubble_messaging";
private static final String ENABLE_AUTO_BUBBLE_ONGOING = "experiment_autobubble_ongoing";
private static final String ENABLE_AUTO_BUBBLE_ALL = "experiment_autobubble_all";
+ private static final String ENABLE_BUBBLE_ACTIVITY_VIEW = "experiment_bubble_activity_view";
+ private static final String ENABLE_BUBBLE_CONTENT_INTENT = "experiment_bubble_content_intent";
private final Context mContext;
private final NotificationEntryManager mNotificationEntryManager;
@@ -189,6 +196,9 @@
// It's new
BubbleView bubble = new BubbleView(mContext);
bubble.setNotif(notif);
+ if (shouldUseActivityView(mContext)) {
+ bubble.setAppOverlayIntent(getAppOverlayIntent(notif));
+ }
mBubbles.put(bubble.getKey(), bubble);
boolean setPosition = mStackView != null && mStackView.getVisibility() != VISIBLE;
@@ -216,6 +226,21 @@
}
}
+ @Nullable
+ private PendingIntent getAppOverlayIntent(NotificationEntry notif) {
+ Notification notification = notif.notification.getNotification();
+ if (canLaunchInActivityView(notification.getAppOverlayIntent())) {
+ return notification.getAppOverlayIntent();
+ } else if (shouldUseContentIntent(mContext)
+ && canLaunchInActivityView(notification.contentIntent)) {
+ Log.d(TAG, "[addBubble " + notif.key
+ + "]: No appOverlayIntent, using contentIntent.");
+ return notification.contentIntent;
+ }
+ Log.d(TAG, "[addBubble " + notif.key + "]: No supported intent for ActivityView.");
+ return null;
+ }
+
/**
* Removes the bubble associated with the {@param uri}.
*/
@@ -223,6 +248,7 @@
BubbleView bv = mBubbles.get(key);
if (mStackView != null && bv != null) {
mStackView.removeBubble(bv);
+ bv.destroyActivityView(mStackView);
bv.getEntry().setBubbleDismissed(true);
}
@@ -282,9 +308,10 @@
}
}
}
- for (BubbleView view : viewsToRemove) {
- mBubbles.remove(view.getKey());
- mStackView.removeBubble(view);
+ for (BubbleView bubbleView : viewsToRemove) {
+ mBubbles.remove(bubbleView.getKey());
+ mStackView.removeBubble(bubbleView);
+ bubbleView.destroyActivityView(mStackView);
}
if (mStackView != null) {
mStackView.setVisibility(visible ? VISIBLE : INVISIBLE);
@@ -306,6 +333,17 @@
return mTempRect;
}
+ private boolean canLaunchInActivityView(PendingIntent intent) {
+ if (intent == null) {
+ return false;
+ }
+ ActivityInfo info =
+ intent.getIntent().resolveActivityInfo(mContext.getPackageManager(), 0);
+ return info != null
+ && ActivityInfo.isResizeableMode(info.resizeMode)
+ && (info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) != 0;
+ }
+
@VisibleForTesting
BubbleStackView getStackView() {
return mStackView;
@@ -378,4 +416,14 @@
return Settings.Secure.getInt(context.getContentResolver(),
ENABLE_AUTO_BUBBLE_ALL, 0) != 0;
}
+
+ private static boolean shouldUseActivityView(Context context) {
+ return Settings.Secure.getInt(context.getContentResolver(),
+ ENABLE_BUBBLE_ACTIVITY_VIEW, 0) != 0;
+ }
+
+ private static boolean shouldUseContentIntent(Context context) {
+ return Settings.Secure.getInt(context.getContentResolver(),
+ ENABLE_BUBBLE_CONTENT_INTENT, 0) != 0;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java
index e28d96b..badefe1 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java
@@ -24,6 +24,7 @@
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;
+import android.widget.TextView;
import com.android.systemui.R;
import com.android.systemui.recents.TriangleShape;
@@ -35,6 +36,8 @@
// The triangle pointing to the expanded view
private View mPointerView;
+ // The view displayed between the pointer and the expanded view
+ private TextView mHeaderView;
// The view that is being displayed for the expanded state
private View mExpandedView;
@@ -68,6 +71,7 @@
TriangleShape.create(width, height, true /* pointUp */));
triangleDrawable.setTint(Color.WHITE); // TODO: dark mode
mPointerView.setBackground(triangleDrawable);
+ mHeaderView = findViewById(R.id.bubble_content_header);
}
/**
@@ -80,9 +84,19 @@
}
/**
+ * Set the text displayed within the header.
+ */
+ public void setHeaderText(CharSequence text) {
+ mHeaderView.setText(text);
+ }
+
+ /**
* Set the view to display for the expanded state. Passing null will clear the view.
*/
public void setExpandedView(View view) {
+ if (mExpandedView == view) {
+ return;
+ }
if (mExpandedView != null) {
removeView(mExpandedView);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index d69e7b3..3280a33 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -21,10 +21,13 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
+import android.app.ActivityView;
+import android.app.PendingIntent;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.RectF;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -50,6 +53,7 @@
*/
public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.FloatingView {
+ private static final String TAG = "BubbleStackView";
private Point mDisplaySize;
private FrameLayout mBubbleContainer;
@@ -59,6 +63,7 @@
private int mBubblePadding;
private boolean mIsExpanded;
+ private int mExpandedBubbleHeight;
private BubbleView mExpandedBubble;
private Point mCollapsedPosition;
private BubbleTouchHandler mTouchHandler;
@@ -106,6 +111,7 @@
mBubbleSize = res.getDimensionPixelSize(R.dimen.bubble_size);
mBubblePadding = res.getDimensionPixelSize(R.dimen.bubble_padding);
+ mExpandedBubbleHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
mDisplaySize = new Point();
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
wm.getDefaultDisplay().getSize(mDisplaySize);
@@ -389,27 +395,63 @@
}
private void updateExpandedBubble() {
- if (mExpandedBubble != null) {
+ if (mExpandedBubble == null) {
+ return;
+ }
+
+ if (mExpandedBubble.hasAppOverlayIntent()) {
+ ActivityView expandedView = mExpandedBubble.getActivityView();
+ expandedView.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, mExpandedBubbleHeight));
+
+ final PendingIntent intent = mExpandedBubble.getAppOverlayIntent();
+ mExpandedViewContainer.setHeaderText(intent.getIntent().getComponent().toShortString());
+ mExpandedViewContainer.setExpandedView(expandedView);
+ expandedView.setCallback(new ActivityView.StateCallback() {
+ @Override
+ public void onActivityViewReady(ActivityView view) {
+ Log.d(TAG, "onActivityViewReady("
+ + mExpandedBubble.getEntry().key + "): " + view);
+ view.startActivity(intent);
+ }
+
+ @Override
+ public void onActivityViewDestroyed(ActivityView view) {
+ NotificationEntry entry = mExpandedBubble.getEntry();
+ Log.d(TAG, "onActivityViewDestroyed(key="
+ + ((entry != null) ? entry.key : "(none)") + "): " + view);
+ }
+ });
+ } else {
ExpandableNotificationRow row = mExpandedBubble.getRowView();
- if (!row.equals(mExpandedViewContainer.getChildAt(0))) {
+ if (!row.equals(mExpandedViewContainer.getExpandedView())) {
// Different expanded view than what we have
mExpandedViewContainer.setExpandedView(null);
}
- int pointerPosition = mExpandedBubble.getPosition().x
- + (mExpandedBubble.getWidth() / 2);
- mExpandedViewContainer.setPointerPosition(pointerPosition);
mExpandedViewContainer.setExpandedView(row);
+ mExpandedViewContainer.setHeaderText(null);
}
+ int pointerPosition = mExpandedBubble.getPosition().x
+ + (mExpandedBubble.getWidth() / 2);
+ mExpandedViewContainer.setPointerPosition(pointerPosition);
}
private void applyCurrentState() {
+ Log.d(TAG, "applyCurrentState: mIsExpanded=" + mIsExpanded);
+
mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
if (!mIsExpanded) {
mExpandedViewContainer.setExpandedView(null);
} else {
mExpandedViewContainer.setTranslationY(mBubbleContainer.getHeight());
- ExpandableNotificationRow row = mExpandedBubble.getRowView();
- applyRowState(row);
+ View expandedView = mExpandedViewContainer.getExpandedView();
+ if (expandedView instanceof ActivityView) {
+ if (expandedView.isAttachedToWindow()) {
+ ((ActivityView) expandedView).onLocationChanged();
+ }
+ } else {
+ applyRowState(mExpandedBubble.getRowView());
+ }
}
int bubbsCount = mBubbleContainer.getChildCount();
for (int i = 0; i < bubbsCount; i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
index 88030ee..96b2dba 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
@@ -33,7 +33,7 @@
* Handles interpreting touches on a {@link BubbleStackView}. This includes expanding, collapsing,
* dismissing, and flings.
*/
-public class BubbleTouchHandler implements View.OnTouchListener {
+class BubbleTouchHandler implements View.OnTouchListener {
private BubbleController mController = Dependency.get(BubbleController.class);
private PipDismissViewController mDismissViewController;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
index 3307992..c1bbb93 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
@@ -16,7 +16,9 @@
package com.android.systemui.bubbles;
+import android.app.ActivityView;
import android.app.Notification;
+import android.app.PendingIntent;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Point;
@@ -25,7 +27,9 @@
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -37,7 +41,7 @@
/**
* A floating object on the screen that has a collapsed and expanded state.
*/
-public class BubbleView extends LinearLayout implements BubbleTouchHandler.FloatingView {
+class BubbleView extends LinearLayout implements BubbleTouchHandler.FloatingView {
private static final String TAG = "BubbleView";
private Context mContext;
@@ -46,6 +50,8 @@
private NotificationEntry mEntry;
private int mBubbleSize;
private int mIconSize;
+ private PendingIntent mAppOverlayIntent;
+ private ActivityView mActivityView;
public BubbleView(Context context) {
this(context, null);
@@ -117,12 +123,51 @@
}
/**
- * @return the view to display when the bubble is expanded.
+ * @return the view to display notification content when the bubble is expanded.
*/
public ExpandableNotificationRow getRowView() {
return mEntry.getRow();
}
+ /**
+ * @return a view used to display app overlay content when expanded.
+ */
+ public ActivityView getActivityView() {
+ if (mActivityView == null) {
+ mActivityView = new ActivityView(mContext);
+ Log.d(TAG, "[getActivityView] created: " + mActivityView);
+ }
+ return mActivityView;
+ }
+
+ /**
+ * Removes and releases an ActivityView if one was previously created for this bubble.
+ */
+ public void destroyActivityView(ViewGroup tmpParent) {
+ if (mActivityView == null) {
+ return;
+ }
+ // HACK: Only release if initialized. There's no way to know if the ActivityView has
+ // been initialized. Calling release() if it hasn't been initialized will crash.
+
+ if (!mActivityView.isAttachedToWindow()) {
+ // HACK: release() will crash if the view is not attached.
+
+ mActivityView.setVisibility(View.GONE);
+ tmpParent.addView(mActivityView, new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+ }
+ try {
+ mActivityView.release();
+ } catch (IllegalStateException ex) {
+ Log.e(TAG, "ActivityView either already released, or not yet initialized.", ex);
+ }
+
+ ((ViewGroup) mActivityView.getParent()).removeView(mActivityView);
+ mActivityView = null;
+ }
+
@Override
public void setPosition(int x, int y) {
setTranslationX(x);
@@ -162,4 +207,20 @@
lp.height = mBubbleSize;
v.setLayoutParams(lp);
}
+
+ /**
+ * @return whether an ActivityView should be used to display the content of this Bubble
+ */
+ public boolean hasAppOverlayIntent() {
+ return mAppOverlayIntent != null;
+ }
+
+ public PendingIntent getAppOverlayIntent() {
+ return mAppOverlayIntent;
+
+ }
+
+ public void setAppOverlayIntent(PendingIntent intent) {
+ mAppOverlayIntent = intent;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
index 6193159..0c63e29 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
@@ -18,6 +18,7 @@
import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
+import android.app.RemoteInput;
import android.content.Context;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -42,14 +43,18 @@
private static final String KEY_REQUIRES_TARGETING_P = "requires_targeting_p";
private static final String KEY_MAX_SQUEEZE_REMEASURE_ATTEMPTS =
"max_squeeze_remeasure_attempts";
+ private static final String KEY_EDIT_CHOICES_BEFORE_SENDING =
+ "edit_choices_before_sending";
private final boolean mDefaultEnabled;
private final boolean mDefaultRequiresP;
private final int mDefaultMaxSqueezeRemeasureAttempts;
+ private final boolean mDefaultEditChoicesBeforeSending;
private boolean mEnabled;
private boolean mRequiresTargetingP;
private int mMaxSqueezeRemeasureAttempts;
+ private boolean mEditChoicesBeforeSending;
private final Context mContext;
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -66,6 +71,8 @@
R.bool.config_smart_replies_in_notifications_requires_targeting_p);
mDefaultMaxSqueezeRemeasureAttempts = resources.getInteger(
R.integer.config_smart_replies_in_notifications_max_squeeze_remeasure_attempts);
+ mDefaultEditChoicesBeforeSending = resources.getBoolean(
+ R.bool.config_smart_replies_in_notifications_edit_choices_before_sending);
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS),
@@ -90,6 +97,8 @@
mRequiresTargetingP = mParser.getBoolean(KEY_REQUIRES_TARGETING_P, mDefaultRequiresP);
mMaxSqueezeRemeasureAttempts = mParser.getInt(
KEY_MAX_SQUEEZE_REMEASURE_ATTEMPTS, mDefaultMaxSqueezeRemeasureAttempts);
+ mEditChoicesBeforeSending = mParser.getBoolean(
+ KEY_EDIT_CHOICES_BEFORE_SENDING, mDefaultEditChoicesBeforeSending);
}
}
@@ -113,4 +122,24 @@
public int getMaxSqueezeRemeasureAttempts() {
return mMaxSqueezeRemeasureAttempts;
}
+
+ /**
+ * Returns whether by tapping on a choice should let the user edit the input before it
+ * is sent to the app.
+ *
+ * @param remoteInputEditChoicesBeforeSending The value from
+ * {@link RemoteInput#getEditChoicesBeforeSending()}
+ */
+ public boolean getEffectiveEditChoicesBeforeSending(
+ @RemoteInput.EditChoicesBeforeSending int remoteInputEditChoicesBeforeSending) {
+ switch (remoteInputEditChoicesBeforeSending) {
+ case RemoteInput.EDIT_CHOICES_BEFORE_SENDING_DISABLED:
+ return false;
+ case RemoteInput.EDIT_CHOICES_BEFORE_SENDING_ENABLED:
+ return true;
+ case RemoteInput.EDIT_CHOICES_BEFORE_SENDING_AUTO:
+ default:
+ return mEditChoicesBeforeSending;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index 68b172d..a76cf16 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -242,9 +242,8 @@
b.setText(choice);
OnDismissAction action = () -> {
- // TODO(b/111437455): Also for EDIT_CHOICES_BEFORE_SENDING_AUTO, depending on flags.
- if (smartReplies.remoteInput.getEditChoicesBeforeSending()
- == RemoteInput.EDIT_CHOICES_BEFORE_SENDING_ENABLED) {
+ if (mConstants.getEffectiveEditChoicesBeforeSending(
+ smartReplies.remoteInput.getEditChoicesBeforeSending())) {
entry.remoteInputText = choice;
mRemoteInputManager.activateRemoteInput(b,
new RemoteInput[] { smartReplies.remoteInput }, smartReplies.remoteInput,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
index 2266b47..37a56a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
@@ -20,6 +20,7 @@
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import android.app.RemoteInput;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
@@ -51,6 +52,8 @@
resources.addOverride(R.bool.config_smart_replies_in_notifications_enabled, true);
resources.addOverride(
R.integer.config_smart_replies_in_notifications_max_squeeze_remeasure_attempts, 7);
+ resources.addOverride(
+ R.bool.config_smart_replies_in_notifications_edit_choices_before_sending, false);
mConstants = new SmartReplyConstants(Handler.createAsync(Looper.myLooper()), mContext);
}
@@ -104,6 +107,51 @@
assertEquals(5, mConstants.getMaxSqueezeRemeasureAttempts());
}
+ @Test
+ public void testGetEffectiveEditChoicesBeforeSendingWithNoConfig() {
+ overrideSetting("enabled=true");
+ triggerConstantsOnChange();
+ assertFalse(
+ mConstants.getEffectiveEditChoicesBeforeSending(
+ RemoteInput.EDIT_CHOICES_BEFORE_SENDING_AUTO));
+ assertTrue(
+ mConstants.getEffectiveEditChoicesBeforeSending(
+ RemoteInput.EDIT_CHOICES_BEFORE_SENDING_ENABLED));
+ assertFalse(
+ mConstants.getEffectiveEditChoicesBeforeSending(
+ RemoteInput.EDIT_CHOICES_BEFORE_SENDING_DISABLED));
+ }
+
+ @Test
+ public void testGetEffectiveEditChoicesBeforeSendingWithEnabledConfig() {
+ overrideSetting("enabled=true,edit_choices_before_sending=true");
+ triggerConstantsOnChange();
+ assertTrue(
+ mConstants.getEffectiveEditChoicesBeforeSending(
+ RemoteInput.EDIT_CHOICES_BEFORE_SENDING_AUTO));
+ assertTrue(
+ mConstants.getEffectiveEditChoicesBeforeSending(
+ RemoteInput.EDIT_CHOICES_BEFORE_SENDING_ENABLED));
+ assertFalse(
+ mConstants.getEffectiveEditChoicesBeforeSending(
+ RemoteInput.EDIT_CHOICES_BEFORE_SENDING_DISABLED));
+ }
+
+ @Test
+ public void testGetEffectiveEditChoicesBeforeSendingWithDisabledConfig() {
+ overrideSetting("enabled=true,edit_choices_before_sending=false");
+ triggerConstantsOnChange();
+ assertFalse(
+ mConstants.getEffectiveEditChoicesBeforeSending(
+ RemoteInput.EDIT_CHOICES_BEFORE_SENDING_AUTO));
+ assertTrue(
+ mConstants.getEffectiveEditChoicesBeforeSending(
+ RemoteInput.EDIT_CHOICES_BEFORE_SENDING_ENABLED));
+ assertFalse(
+ mConstants.getEffectiveEditChoicesBeforeSending(
+ RemoteInput.EDIT_CHOICES_BEFORE_SENDING_DISABLED));
+ }
+
private void overrideSetting(String flags) {
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS, flags);
diff --git a/packages/overlays/FontArbutusSourceOverlay/res/values/config.xml b/packages/overlays/FontArbutusSourceOverlay/res/values/config.xml
index 165aab2..a6aa64c 100644
--- a/packages/overlays/FontArbutusSourceOverlay/res/values/config.xml
+++ b/packages/overlays/FontArbutusSourceOverlay/res/values/config.xml
@@ -19,7 +19,7 @@
<!-- Name of a font family to use for body text. -->
<string name="config_bodyFontFamily" translatable="false">source-sans-pro</string>
<!-- Name of a font family to use for medium body text. -->
- <string name="config_bodyFontFamilyMedium" translatable="false">source-sans-pro-medium</string>
+ <string name="config_bodyFontFamilyMedium" translatable="false">source-sans-pro-semi-bold</string>
<!-- Name of a font family to use for headlines. If empty, falls back to platform default -->
<string name="config_headlineFontFamily" translatable="false">arbutus-slab</string>
<!-- Name of the font family used for system surfaces where the font should use medium weight -->
diff --git a/packages/overlays/FontArvoLatoOverlay/res/values/config.xml b/packages/overlays/FontArvoLatoOverlay/res/values/config.xml
index 229c578..4e70d72 100644
--- a/packages/overlays/FontArvoLatoOverlay/res/values/config.xml
+++ b/packages/overlays/FontArvoLatoOverlay/res/values/config.xml
@@ -19,10 +19,10 @@
<!-- Name of a font family to use for body text. -->
<string name="config_bodyFontFamily" translatable="false">lato</string>
<!-- Name of a font family to use for medium body text. -->
- <string name="config_bodyFontFamilyMedium" translatable="false">lato-medium</string>
+ <string name="config_bodyFontFamilyMedium" translatable="false">lato-bold</string>
<!-- Name of a font family to use for headlines. If empty, falls back to platform default -->
<string name="config_headlineFontFamily" translatable="false">arvo</string>
<!-- Name of the font family used for system surfaces where the font should use medium weight -->
- <string name="config_headlineFontFamilyMedium" translatable="false">arvo-medium</string>
+ <string name="config_headlineFontFamilyMedium" translatable="false">arvo-bold</string>
</resources>
diff --git a/packages/services/PacProcessor/jni/com_android_pacprocessor_PacNative.cpp b/packages/services/PacProcessor/jni/com_android_pacprocessor_PacNative.cpp
index a40cf1c..d969c69 100644
--- a/packages/services/PacProcessor/jni/com_android_pacprocessor_PacNative.cpp
+++ b/packages/services/PacProcessor/jni/com_android_pacprocessor_PacNative.cpp
@@ -16,6 +16,9 @@
#define LOG_TAG "PacProcessor"
+#include <stdlib.h>
+#include <string>
+
#include <utils/Log.h>
#include <utils/Mutex.h>
#include "android_runtime/AndroidRuntime.h"
@@ -23,24 +26,24 @@
#include "jni.h"
#include <nativehelper/JNIHelp.h>
-#include "proxy_resolver_v8.h"
+#include "proxy_resolver_v8_wrapper.h"
namespace android {
-net::ProxyResolverV8* proxyResolver = NULL;
+ProxyResolverV8Handle* proxyResolver = NULL;
bool pacSet = false;
-String16 jstringToString16(JNIEnv* env, jstring jstr) {
+std::u16string jstringToString16(JNIEnv* env, jstring jstr) {
const jchar* str = env->GetStringCritical(jstr, 0);
- String16 str16(reinterpret_cast<const char16_t*>(str),
+ std::u16string str16(reinterpret_cast<const char16_t*>(str),
env->GetStringLength(jstr));
env->ReleaseStringCritical(jstr, str);
return str16;
}
-jstring string16ToJstring(JNIEnv* env, String16 string) {
- const char16_t* str = string.string();
- size_t len = string.size();
+jstring string16ToJstring(JNIEnv* env, std::u16string string) {
+ const char16_t* str = string.data();
+ size_t len = string.length();
return env->NewString(reinterpret_cast<const jchar*>(str), len);
}
@@ -48,7 +51,7 @@
static jboolean com_android_pacprocessor_PacNative_createV8ParserNativeLocked(JNIEnv* /* env */,
jobject) {
if (proxyResolver == NULL) {
- proxyResolver = new net::ProxyResolverV8(net::ProxyResolverJSBindings::CreateDefault());
+ proxyResolver = ProxyResolverV8Handle_new();
pacSet = false;
return JNI_FALSE;
}
@@ -58,7 +61,7 @@
static jboolean com_android_pacprocessor_PacNative_destroyV8ParserNativeLocked(JNIEnv* /* env */,
jobject) {
if (proxyResolver != NULL) {
- delete proxyResolver;
+ ProxyResolverV8Handle_delete(proxyResolver);
proxyResolver = NULL;
return JNI_FALSE;
}
@@ -67,14 +70,14 @@
static jboolean com_android_pacprocessor_PacNative_setProxyScriptNativeLocked(JNIEnv* env, jobject,
jstring script) {
- String16 script16 = jstringToString16(env, script);
+ std::u16string script16 = jstringToString16(env, script);
if (proxyResolver == NULL) {
ALOGE("V8 Parser not started when setting PAC script");
return JNI_TRUE;
}
- if (proxyResolver->SetPacScript(script16) != OK) {
+ if (ProxyResolverV8Handle_SetPacScript(proxyResolver, script16.data()) != OK) {
ALOGE("Unable to set PAC script");
return JNI_TRUE;
}
@@ -85,9 +88,8 @@
static jstring com_android_pacprocessor_PacNative_makeProxyRequestNativeLocked(JNIEnv* env, jobject,
jstring url, jstring host) {
- String16 url16 = jstringToString16(env, url);
- String16 host16 = jstringToString16(env, host);
- String16 ret;
+ std::u16string url16 = jstringToString16(env, url);
+ std::u16string host16 = jstringToString16(env, host);
if (proxyResolver == NULL) {
ALOGE("V8 Parser not initialized when running PAC script");
@@ -99,12 +101,14 @@
return NULL;
}
- if (proxyResolver->GetProxyForURL(url16, host16, &ret) != OK) {
- String8 ret8(ret);
- ALOGE("Error Running PAC: %s", ret8.string());
+ std::unique_ptr<char16_t, decltype(&free)> result = std::unique_ptr<char16_t, decltype(&free)>(
+ ProxyResolverV8Handle_GetProxyForURL(proxyResolver, url16.data(), host16.data()), &free);
+ if (result.get() == NULL) {
+ ALOGE("Error Running PAC");
return NULL;
}
+ std::u16string ret(result.get());
jstring jret = string16ToJstring(env, ret);
return jret;
diff --git a/services/art-profile b/services/art-profile
index af9d7a9..7e4884b2 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -6970,6 +6970,7 @@
PLcom/android/server/am/ActivityManagerService$LocalService;->getMemoryStateForProcesses()Ljava/util/List;
PLcom/android/server/am/ActivityManagerService$LocalService;->getTopVisibleActivities()Ljava/util/List;
PLcom/android/server/am/ActivityManagerService$LocalService;->getUidProcessState(I)I
+PLcom/android/server/am/ActivityManagerService$LocalService;->isAppForeground(I)Z
PLcom/android/server/am/ActivityManagerService$LocalService;->isCallerRecents(I)Z
PLcom/android/server/am/ActivityManagerService$LocalService;->isRuntimeRestarted()Z
PLcom/android/server/am/ActivityManagerService$LocalService;->isSystemReady()Z
@@ -7125,7 +7126,6 @@
PLcom/android/server/am/ActivityManagerService;->installEncryptionUnawareProviders(I)V
PLcom/android/server/am/ActivityManagerService;->installSystemProviders()V
PLcom/android/server/am/ActivityManagerService;->isAllowedWhileBooting(Landroid/content/pm/ApplicationInfo;)Z
-PLcom/android/server/am/ActivityManagerService;->isAppForeground(I)Z
PLcom/android/server/am/ActivityManagerService;->isAssistDataAllowedOnCurrentActivity()Z
PLcom/android/server/am/ActivityManagerService;->isGetTasksAllowed(Ljava/lang/String;II)Z
PLcom/android/server/am/ActivityManagerService;->isInLockTaskMode()Z
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index d7a2365..6b0adfb 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -850,19 +850,29 @@
mFullBackupQueue = readFullBackupSchedule();
}
- // Register for broadcasts about package install, etc., so we can
- // update the provider list.
+ // Register for broadcasts about package changes.
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addDataScheme("package");
- mContext.registerReceiver(mBroadcastReceiver, filter);
+ mContext.registerReceiverAsUser(
+ mBroadcastReceiver,
+ UserHandle.of(mUserId),
+ filter,
+ /* broadcastPermission */ null,
+ /* scheduler */ null);
+
// Register for events related to sdcard installation.
IntentFilter sdFilter = new IntentFilter();
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
- mContext.registerReceiver(mBroadcastReceiver, sdFilter);
+ mContext.registerReceiverAsUser(
+ mBroadcastReceiver,
+ UserHandle.of(mUserId),
+ sdFilter,
+ /* broadcastPermission */ null,
+ /* scheduler */ null);
}
private ArrayList<FullBackupEntry> readFullBackupSchedule() {
@@ -1127,17 +1137,23 @@
}
}
- // ----- Track installation/removal of packages -----
+ /**
+ * A {@link BroadcastReceiver} tracking changes to packages and sd cards in order to update our
+ * internal bookkeeping.
+ */
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
- if (MORE_DEBUG) Slog.d(TAG, "Received broadcast " + intent);
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Received broadcast " + intent);
+ }
String action = intent.getAction();
boolean replacing = false;
boolean added = false;
boolean changed = false;
Bundle extras = intent.getExtras();
- String[] pkgList = null;
+ String[] packageList = null;
+
if (Intent.ACTION_PACKAGE_ADDED.equals(action)
|| Intent.ACTION_PACKAGE_REMOVED.equals(action)
|| Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
@@ -1145,69 +1161,70 @@
if (uri == null) {
return;
}
- final String pkgName = uri.getSchemeSpecificPart();
- if (pkgName != null) {
- pkgList = new String[]{pkgName};
- }
- changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
- // At package-changed we only care about looking at new transport states
+ String packageName = uri.getSchemeSpecificPart();
+ if (packageName != null) {
+ packageList = new String[]{packageName};
+ }
+
+ changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
if (changed) {
- final String[] components =
+ // Look at new transport states for package changed events.
+ String[] components =
intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
if (MORE_DEBUG) {
- Slog.i(TAG, "Package " + pkgName + " changed; rechecking");
+ Slog.i(TAG, "Package " + packageName + " changed");
for (int i = 0; i < components.length; i++) {
Slog.i(TAG, " * " + components[i]);
}
}
mBackupHandler.post(
- () -> mTransportManager.onPackageChanged(pkgName, components));
- return; // nothing more to do in the PACKAGE_CHANGED case
+ () -> mTransportManager.onPackageChanged(packageName, components));
+ return;
}
added = Intent.ACTION_PACKAGE_ADDED.equals(action);
replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
} else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
added = true;
- pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ packageList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
} else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
added = false;
- pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ packageList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
}
- if (pkgList == null || pkgList.length == 0) {
+ if (packageList == null || packageList.length == 0) {
return;
}
- final int uid = extras.getInt(Intent.EXTRA_UID);
+ int uid = extras.getInt(Intent.EXTRA_UID);
if (added) {
synchronized (mBackupParticipants) {
if (replacing) {
- // This is the package-replaced case; we just remove the entry
- // under the old uid and fall through to re-add. If an app
- // just added key/value backup participation, this picks it up
- // as a known participant.
- removePackageParticipantsLocked(pkgList, uid);
+ // Remove the entry under the old uid and fall through to re-add. If an app
+ // just opted into key/value backup, add it as a known participant.
+ removePackageParticipantsLocked(packageList, uid);
}
- addPackageParticipantsLocked(pkgList);
+ addPackageParticipantsLocked(packageList);
}
- // If they're full-backup candidates, add them there instead
- final long now = System.currentTimeMillis();
- for (final String packageName : pkgList) {
+
+ long now = System.currentTimeMillis();
+ for (String packageName : packageList) {
try {
- PackageInfo app = mPackageManager.getPackageInfo(packageName, 0);
+ PackageInfo app =
+ mPackageManager.getPackageInfoAsUser(
+ packageName, /* flags */ 0, mUserId);
if (AppBackupUtils.appGetsFullBackup(app)
&& AppBackupUtils.appIsEligibleForBackup(
app.applicationInfo, mPackageManager)) {
enqueueFullBackup(packageName, now);
scheduleNextFullBackupJob(0);
} else {
- // The app might have just transitioned out of full-data into
- // doing key/value backups, or might have just disabled backups
- // entirely. Make sure it is no longer in the full-data queue.
+ // The app might have just transitioned out of full-data into doing
+ // key/value backups, or might have just disabled backups entirely. Make
+ // sure it is no longer in the full-data queue.
synchronized (mQueueLock) {
dequeueFullBackupLocked(packageName);
}
@@ -1216,32 +1233,28 @@
mBackupHandler.post(
() -> mTransportManager.onPackageAdded(packageName));
-
} catch (NameNotFoundException e) {
- // doesn't really exist; ignore it
if (DEBUG) {
Slog.w(TAG, "Can't resolve new app " + packageName);
}
}
}
- // Whenever a package is added or updated we need to update
- // the package metadata bookkeeping.
+ // Whenever a package is added or updated we need to update the package metadata
+ // bookkeeping.
dataChangedImpl(PACKAGE_MANAGER_SENTINEL);
} else {
- if (replacing) {
- // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
- } else {
- // Outright removal. In the full-data case, the app will be dropped
- // from the queue when its (now obsolete) name comes up again for
- // backup.
+ if (!replacing) {
+ // Outright removal. In the full-data case, the app will be dropped from the
+ // queue when its (now obsolete) name comes up again for backup.
synchronized (mBackupParticipants) {
- removePackageParticipantsLocked(pkgList, uid);
+ removePackageParticipantsLocked(packageList, uid);
}
}
- for (final String pkgName : pkgList) {
+
+ for (String packageName : packageList) {
mBackupHandler.post(
- () -> mTransportManager.onPackageRemoved(pkgName));
+ () -> mTransportManager.onPackageRemoved(packageName));
}
}
}
diff --git a/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java b/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java
index bace1aa..d13f711 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java
@@ -7,6 +7,7 @@
import static com.android.server.backup.UserBackupManagerService.BACKUP_WIDGET_METADATA_TOKEN;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.backup.FullBackup;
import android.app.backup.FullBackupDataOutput;
import android.content.pm.PackageInfo;
@@ -15,7 +16,6 @@
import android.content.pm.SigningInfo;
import android.os.Build;
import android.os.Environment;
-import android.os.UserHandle;
import android.util.Log;
import android.util.StringBuilderPrinter;
@@ -254,12 +254,11 @@
* for 'adb backup'.
*/
// TODO(b/113807190): Investigate and potentially remove.
- public void backupObb(PackageInfo packageInfo) {
+ public void backupObb(@UserIdInt int userId, PackageInfo packageInfo) {
// TODO: migrate this to SharedStorageBackup, since AID_SYSTEM doesn't have access to
// external storage.
- // TODO: http://b/22388012
Environment.UserEnvironment userEnv =
- new Environment.UserEnvironment(UserHandle.USER_SYSTEM);
+ new Environment.UserEnvironment(userId);
File obbDir = userEnv.buildExternalStorageAppObbDirs(packageInfo.packageName)[0];
if (obbDir != null) {
if (MORE_DEBUG) {
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
index 03d4e97..7ea1892 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
@@ -54,12 +54,12 @@
*/
public class FullBackupEngine {
private UserBackupManagerService backupManagerService;
- OutputStream mOutput;
- FullBackupPreflight mPreflightHook;
- BackupRestoreTask mTimeoutMonitor;
- IBackupAgent mAgent;
- boolean mIncludeApks;
- PackageInfo mPkg;
+ private OutputStream mOutput;
+ private FullBackupPreflight mPreflightHook;
+ private BackupRestoreTask mTimeoutMonitor;
+ private IBackupAgent mAgent;
+ private boolean mIncludeApks;
+ private PackageInfo mPkg;
private final long mQuota;
private final int mOpToken;
private final int mTransportFlags;
@@ -130,7 +130,7 @@
// TODO(b/113807190): Look into removing, only used for 'adb backup'.
if (writeApk) {
appMetadataBackupWriter.backupApk(mPackage);
- appMetadataBackupWriter.backupObb(mPackage);
+ appMetadataBackupWriter.backupObb(mUserId, mPackage);
}
if (DEBUG) {
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
index 43a80c4..31786d7 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
@@ -66,24 +66,22 @@
*/
public class PerformAdbBackupTask extends FullBackupTask implements BackupRestoreTask {
- private UserBackupManagerService backupManagerService;
- FullBackupEngine mBackupEngine;
- final AtomicBoolean mLatch;
+ private final UserBackupManagerService mUserBackupManagerService;
+ private final AtomicBoolean mLatch;
- ParcelFileDescriptor mOutputFile;
- DeflaterOutputStream mDeflater;
- boolean mIncludeApks;
- boolean mIncludeObbs;
- boolean mIncludeShared;
- boolean mDoWidgets;
- boolean mAllApps;
- boolean mIncludeSystem;
- boolean mCompress;
- boolean mKeyValue;
- ArrayList<String> mPackages;
- PackageInfo mCurrentTarget;
- String mCurrentPassword;
- String mEncryptPassword;
+ private final ParcelFileDescriptor mOutputFile;
+ private final boolean mIncludeApks;
+ private final boolean mIncludeObbs;
+ private final boolean mIncludeShared;
+ private final boolean mDoWidgets;
+ private final boolean mAllApps;
+ private final boolean mIncludeSystem;
+ private final boolean mCompress;
+ private final boolean mKeyValue;
+ private final ArrayList<String> mPackages;
+ private PackageInfo mCurrentTarget;
+ private final String mCurrentPassword;
+ private final String mEncryptPassword;
private final int mCurrentOpToken;
public PerformAdbBackupTask(UserBackupManagerService backupManagerService,
@@ -92,7 +90,7 @@
String curPassword, String encryptPassword, boolean doAllApps, boolean doSystem,
boolean doCompress, boolean doKeyValue, String[] packages, AtomicBoolean latch) {
super(observer);
- this.backupManagerService = backupManagerService;
+ mUserBackupManagerService = backupManagerService;
mCurrentOpToken = backupManagerService.generateRandomIntegerToken();
mLatch = latch;
@@ -104,7 +102,7 @@
mAllApps = doAllApps;
mIncludeSystem = doSystem;
mPackages = (packages == null)
- ? new ArrayList<String>()
+ ? new ArrayList<>()
: new ArrayList<>(Arrays.asList(packages));
mCurrentPassword = curPassword;
// when backing up, if there is a current backup password, we require that
@@ -123,11 +121,11 @@
mKeyValue = doKeyValue;
}
- void addPackagesToSet(TreeMap<String, PackageInfo> set, List<String> pkgNames) {
+ private void addPackagesToSet(TreeMap<String, PackageInfo> set, List<String> pkgNames) {
for (String pkgName : pkgNames) {
if (!set.containsKey(pkgName)) {
try {
- PackageInfo info = backupManagerService.getPackageManager().getPackageInfo(
+ PackageInfo info = mUserBackupManagerService.getPackageManager().getPackageInfo(
pkgName,
PackageManager.GET_SIGNING_CERTIFICATES);
set.put(pkgName, info);
@@ -141,7 +139,7 @@
private OutputStream emitAesBackupHeader(StringBuilder headerbuf,
OutputStream ofstream) throws Exception {
// User key will be used to encrypt the master key.
- byte[] newUserSalt = backupManagerService
+ byte[] newUserSalt = mUserBackupManagerService
.randomBytes(PasswordUtils.PBKDF2_SALT_SIZE);
SecretKey userKey = PasswordUtils
.buildPasswordKey(PBKDF_CURRENT, mEncryptPassword,
@@ -150,8 +148,8 @@
// the master key is random for each backup
byte[] masterPw = new byte[256 / 8];
- backupManagerService.getRng().nextBytes(masterPw);
- byte[] checksumSalt = backupManagerService
+ mUserBackupManagerService.getRng().nextBytes(masterPw);
+ byte[] checksumSalt = mUserBackupManagerService
.randomBytes(PasswordUtils.PBKDF2_SALT_SIZE);
// primary encryption of the datastream with the random key
@@ -232,11 +230,11 @@
TreeMap<String, PackageInfo> packagesToBackup = new TreeMap<>();
FullBackupObbConnection obbConnection = new FullBackupObbConnection(
- backupManagerService);
+ mUserBackupManagerService);
obbConnection.establish(); // we'll want this later
sendStartBackup();
- PackageManager pm = backupManagerService.getPackageManager();
+ PackageManager pm = mUserBackupManagerService.getPackageManager();
// doAllApps supersedes the package set if any
if (mAllApps) {
@@ -245,7 +243,7 @@
for (int i = 0; i < allPackages.size(); i++) {
PackageInfo pkg = allPackages.get(i);
// Exclude system apps if we've been asked to do so
- if (mIncludeSystem == true
+ if (mIncludeSystem
|| ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0)) {
packagesToBackup.put(pkg.packageName, pkg);
}
@@ -316,7 +314,7 @@
boolean encrypting = (mEncryptPassword != null && mEncryptPassword.length() > 0);
// Only allow encrypted backups of encrypted devices
- if (backupManagerService.deviceIsEncrypted() && !encrypting) {
+ if (mUserBackupManagerService.deviceIsEncrypted() && !encrypting) {
Slog.e(TAG, "Unencrypted backup of encrypted device; aborting");
return;
}
@@ -325,7 +323,7 @@
// Verify that the given password matches the currently-active
// backup password, if any
- if (!backupManagerService.backupPasswordMatches(mCurrentPassword)) {
+ if (!mUserBackupManagerService.backupPasswordMatches(mCurrentPassword)) {
if (DEBUG) {
Slog.w(TAG, "Backup password mismatch; aborting");
}
@@ -390,7 +388,7 @@
// Shared storage if requested
if (mIncludeShared) {
try {
- pkg = backupManagerService.getPackageManager().getPackageInfo(
+ pkg = mUserBackupManagerService.getPackageManager().getPackageInfo(
SHARED_BACKUP_AGENT_PACKAGE, 0);
backupQueue.add(pkg);
} catch (NameNotFoundException e) {
@@ -410,9 +408,17 @@
pkg.packageName.equals(
SHARED_BACKUP_AGENT_PACKAGE);
- mBackupEngine = new FullBackupEngine(backupManagerService, out,
- null, pkg, mIncludeApks, this, Long.MAX_VALUE,
- mCurrentOpToken, /*transportFlags=*/ 0);
+ FullBackupEngine mBackupEngine =
+ new FullBackupEngine(
+ mUserBackupManagerService,
+ out,
+ null,
+ pkg,
+ mIncludeApks,
+ this,
+ Long.MAX_VALUE,
+ mCurrentOpToken,
+ /*transportFlags=*/ 0);
sendOnBackupPackage(isSharedStorage ? "Shared storage" : pkg.packageName);
// Don't need to check preflight result as there is no preflight hook.
@@ -437,10 +443,10 @@
}
KeyValueAdbBackupEngine kvBackupEngine =
new KeyValueAdbBackupEngine(out, keyValuePackage,
- backupManagerService,
- backupManagerService.getPackageManager(),
- backupManagerService.getBaseStateDir(),
- backupManagerService.getDataDir());
+ mUserBackupManagerService,
+ mUserBackupManagerService.getPackageManager(),
+ mUserBackupManagerService.getBaseStateDir(),
+ mUserBackupManagerService.getDataDir());
sendOnBackupPackage(keyValuePackage.packageName);
kvBackupEngine.backupOnePackage();
}
@@ -471,7 +477,7 @@
if (DEBUG) {
Slog.d(TAG, "Full backup pass complete.");
}
- backupManagerService.getWakelock().release();
+ mUserBackupManagerService.getWakelock().release();
}
}
@@ -493,8 +499,8 @@
Slog.w(TAG, "adb backup cancel of " + target);
}
if (target != null) {
- backupManagerService.tearDownAgentAndKill(mCurrentTarget.applicationInfo);
+ mUserBackupManagerService.tearDownAgentAndKill(mCurrentTarget.applicationInfo);
}
- backupManagerService.removeOperation(mCurrentOpToken);
+ mUserBackupManagerService.removeOperation(mCurrentOpToken);
}
}
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 5cea56e..2346cfc 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -64,6 +64,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.os.Message;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
@@ -86,7 +87,6 @@
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
-import com.android.internal.util.Preconditions;
import com.android.server.location.AbstractLocationProvider;
import com.android.server.location.ActivityRecognitionProxy;
import com.android.server.location.GeocoderProxy;
@@ -117,11 +117,6 @@
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executors;
-import java.util.concurrent.FutureTask;
/**
* The service class that manages LocationProviders and issues location
@@ -176,7 +171,10 @@
private static final LocationRequest DEFAULT_LOCATION_REQUEST = new LocationRequest();
private final Context mContext;
- private AppOpsManager mAppOps;
+ private final AppOpsManager mAppOps;
+
+ // used internally for synchronization
+ private final Object mLock = new Object();
// --- fields below are final after systemRunning() ---
private LocationFudger mLocationFudger;
@@ -188,7 +186,7 @@
private GeocoderProxy mGeocodeProvider;
private GnssStatusListenerHelper mGnssStatusProvider;
private INetInitiatedListener mNetInitiatedListener;
- private final Handler mHandler;
+ private LocationWorkerHandler mLocationHandler;
private PassiveProvider mPassiveProvider; // track passive provider for special cases
private LocationBlacklist mBlacklist;
private GnssMeasurementsProvider mGnssMeasurementsProvider;
@@ -197,6 +195,8 @@
private boolean mLocationControllerExtraPackageEnabled;
private IGpsGeofenceHardware mGpsGeofenceProxy;
+ // --- fields below are protected by mLock ---
+
// Mock (test) providers
private final HashMap<String, MockProvider> mMockProviders =
new HashMap<>();
@@ -205,8 +205,8 @@
private final HashMap<Object, Receiver> mReceivers = new HashMap<>();
// currently installed providers (with mocks replacing real providers)
- private final CopyOnWriteArrayList<LocationProvider> mProviders =
- new CopyOnWriteArrayList<>();
+ private final ArrayList<LocationProvider> mProviders =
+ new ArrayList<>();
// real providers, saved here when mocked out
private final HashMap<String, LocationProvider> mRealProviders =
@@ -232,8 +232,8 @@
// all providers that operate over proxy, for authorizing incoming location and whitelisting
// throttling
- private final CopyOnWriteArrayList<LocationProviderProxy> mProxyProviders =
- new CopyOnWriteArrayList<>();
+ private final ArrayList<LocationProviderProxy> mProxyProviders =
+ new ArrayList<>();
private final ArraySet<String> mBackgroundThrottlePackageWhitelist = new ArraySet<>();
@@ -246,6 +246,9 @@
private int mCurrentUserId = UserHandle.USER_SYSTEM;
private int[] mCurrentUserProfiles = new int[]{UserHandle.USER_SYSTEM};
+ // Maximum age of last location returned to clients with foreground-only location permissions.
+ private long mLastLocationMaxAgeMs;
+
private GnssLocationProvider.GnssSystemInfoProvider mGnssSystemInfoProvider;
private GnssLocationProvider.GnssMetricsProvider mGnssMetricsProvider;
@@ -258,7 +261,7 @@
public LocationManagerService(Context context) {
super();
mContext = context;
- mHandler = BackgroundThread.getHandler();
+ mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
// Let the package manager query which are the default location
// providers as they get certain permissions granted by default.
@@ -268,92 +271,132 @@
userId -> mContext.getResources().getStringArray(
com.android.internal.R.array.config_locationProviderPackageNames));
+ if (D) Log.d(TAG, "Constructed");
+
// most startup is deferred until systemRunning()
}
public void systemRunning() {
- runInternal(this::initialize);
- }
+ synchronized (mLock) {
+ if (D) Log.d(TAG, "systemRunning()");
- private void initialize() {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+ // fetch package manager
+ mPackageManager = mContext.getPackageManager();
- mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
- mPackageManager = mContext.getPackageManager();
- mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
+ // fetch power manager
+ mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- // prepare mHandler's dependents
- mLocationFudger = new LocationFudger(mContext, mHandler);
- mBlacklist = new LocationBlacklist(mContext, mHandler);
- mBlacklist.init();
- mGeofenceManager = new GeofenceManager(mContext, mBlacklist);
+ // fetch activity manager
+ mActivityManager
+ = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
- mAppOps.startWatchingMode(
- AppOpsManager.OP_COARSE_LOCATION,
- null,
- AppOpsManager.WATCH_FOREGROUND_CHANGES,
- new AppOpsManager.OnOpChangedInternalListener() {
- public void onOpChanged(int op, String packageName) {
- mHandler.post(() -> onAppOpChanged());
- }
- });
+ // prepare worker thread
+ mLocationHandler = new LocationWorkerHandler(BackgroundThread.get().getLooper());
- mPackageManager.addOnPermissionsChangeListener(
- uid -> runInternal(this::onPermissionsChanged));
+ // prepare mLocationHandler's dependents
+ mLocationFudger = new LocationFudger(mContext, mLocationHandler);
+ mBlacklist = new LocationBlacklist(mContext, mLocationHandler);
+ mBlacklist.init();
+ mGeofenceManager = new GeofenceManager(mContext, mBlacklist);
- mActivityManager.addOnUidImportanceListener(
- (uid, importance) -> mHandler.post(
- () -> onUidImportanceChanged(uid, importance)),
- FOREGROUND_IMPORTANCE_CUTOFF);
+ // Monitor for app ops mode changes.
+ AppOpsManager.OnOpChangedListener callback
+ = new AppOpsManager.OnOpChangedInternalListener() {
+ public void onOpChanged(int op, String packageName) {
+ mLocationHandler.post(() -> {
+ synchronized (mLock) {
+ for (Receiver receiver : mReceivers.values()) {
+ receiver.updateMonitoring(true);
+ }
+ applyAllProviderRequirementsLocked();
+ }
+ });
+ }
+ };
+ mAppOps.startWatchingMode(AppOpsManager.OP_COARSE_LOCATION, null,
+ AppOpsManager.WATCH_FOREGROUND_CHANGES, callback);
- mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- updateUserProfiles(mCurrentUserId);
+ PackageManager.OnPermissionsChangedListener permissionListener = uid -> {
+ synchronized (mLock) {
+ applyAllProviderRequirementsLocked();
+ }
+ };
+ mPackageManager.addOnPermissionsChangeListener(permissionListener);
- updateBackgroundThrottlingWhitelist();
+ // listen for background/foreground changes
+ ActivityManager.OnUidImportanceListener uidImportanceListener =
+ (uid, importance) -> mLocationHandler.post(
+ () -> onUidImportanceChanged(uid, importance));
+ mActivityManager.addOnUidImportanceListener(uidImportanceListener,
+ FOREGROUND_IMPORTANCE_CUTOFF);
- // prepare providers
- loadProvidersLocked();
- updateProvidersSettings();
- for (LocationProvider provider : mProviders) {
- applyRequirements(provider.getName());
+ mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ updateUserProfiles(mCurrentUserId);
+
+ updateBackgroundThrottlingWhitelistLocked();
+ updateLastLocationMaxAgeLocked();
+
+ // prepare providers
+ loadProvidersLocked();
+ updateProvidersSettingsLocked();
+ for (LocationProvider provider : mProviders) {
+ applyRequirementsLocked(provider.getName());
+ }
}
// listen for settings changes
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.LOCATION_PROVIDERS_ALLOWED), true,
- new ContentObserver(mHandler) {
+ new ContentObserver(mLocationHandler) {
@Override
public void onChange(boolean selfChange) {
- onProviderAllowedChanged();
+ synchronized (mLock) {
+ updateProvidersSettingsLocked();
+ }
}
}, UserHandle.USER_ALL);
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS),
true,
- new ContentObserver(mHandler) {
+ new ContentObserver(mLocationHandler) {
@Override
public void onChange(boolean selfChange) {
- onBackgroundThrottleIntervalChanged();
+ synchronized (mLock) {
+ for (LocationProvider provider : mProviders) {
+ applyRequirementsLocked(provider.getName());
+ }
+ }
}
}, UserHandle.USER_ALL);
mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS),
+ true,
+ new ContentObserver(mLocationHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ synchronized (mLock) {
+ updateLastLocationMaxAgeLocked();
+ }
+ }
+ }
+ );
+ mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST),
true,
- new ContentObserver(mHandler) {
+ new ContentObserver(mLocationHandler) {
@Override
public void onChange(boolean selfChange) {
- onBackgroundThrottleWhitelistChanged();
+ synchronized (mLock) {
+ updateBackgroundThrottlingWhitelistLocked();
+ for (LocationProvider provider : mProviders) {
+ applyRequirementsLocked(provider.getName());
+ }
+ }
}
}, UserHandle.USER_ALL);
- new PackageMonitor() {
- @Override
- public void onPackageDisappeared(String packageName, int reason) {
- LocationManagerService.this.onPackageDisappeared(packageName);
- }
- }.register(mContext, mHandler.getLooper(), true);
+ mPackageMonitor.register(mContext, mLocationHandler.getLooper(), true);
// listen for user change
IntentFilter intentFilter = new IntentFilter();
@@ -372,170 +415,73 @@
updateUserProfiles(mCurrentUserId);
}
}
- }, UserHandle.ALL, intentFilter, null, mHandler);
- }
-
- // will block until completion and propagate exceptions, and thus should be used from binder
- // threads, particuarily binder threads from components that sit above LMS (ie, not location
- // providers).
- private void runFromBinderBlocking(Runnable runnable) throws RemoteException {
- runFromBinderBlocking(Executors.callable(runnable));
- }
-
- // will block until completion and propagate exceptions, and thus should be used from binder
- // threads, particuarily binder threads from components that sit above LMS (ie, not location
- // providers).
- private <T> T runFromBinderBlocking(Callable<T> callable) throws RemoteException {
- FutureTask<T> task = new FutureTask<>(callable);
- long identity = Binder.clearCallingIdentity();
- try {
- runInternal(task);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- try {
- return task.get();
- } catch (ExecutionException e) {
- // binder calls can handle 3 types of exceptions, runtimeexception and error (which can
- // be thrown any time), and remote exception. we transfer all of these exceptions from
- // the execution thread to the binder thread so that they may propagate normally. note
- // that we are loosing some context in doing so (losing the stack trace from the binder
- // thread).
- if (e.getCause() instanceof RemoteException) {
- throw (RemoteException) e.getCause();
- } else if (e.getCause() instanceof RuntimeException) {
- throw (RuntimeException) e.getCause();
- } else if (e.getCause() instanceof Error) {
- throw (Error) e.getCause();
- }
-
- // callers should not throw checked exceptions
- Log.wtf(TAG, "caller threw checked exception", e);
- throw new UnsupportedOperationException(e);
- } catch (InterruptedException e) {
- throw new RemoteException("Binder call interrupted", e, true, true);
- }
- }
-
- // will return immediately and will not propagate exceptions. should be used for non-binder work
- // that needs to be shifted onto the location thread, primarily listeners that do not support
- // running on arbitrary threads.
- private void runInternal(Runnable runnable) {
- // it would be a better use of resources to use locks to manage cross thread access to
- // various pieces on information. however, the history of the location package has mostly
- // shown that this is difficult to maintain in a multi-dev environment, and tends to always
- // lead towards the use of uber-locks and deadlocks. using a single thread to run everything
- // is more understandable for most devs, and seems less likely to result in future bugs
- if (Looper.myLooper() == mHandler.getLooper()) {
- runnable.run();
- } else {
- mHandler.post(runnable);
- }
- }
-
- private void onAppOpChanged() {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
- for (Receiver receiver : mReceivers.values()) {
- receiver.updateMonitoring(true);
- }
- for (LocationProvider p : mProviders) {
- applyRequirements(p.getName());
- }
- }
-
- private void onPermissionsChanged() {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
- for (LocationProvider p : mProviders) {
- applyRequirements(p.getName());
- }
- }
-
- private void onProviderAllowedChanged() {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
- updateProvidersSettings();
- }
-
- private void onPackageDisappeared(String packageName) {
- ArrayList<Receiver> deadReceivers = null;
-
- for (Receiver receiver : mReceivers.values()) {
- if (receiver.mIdentity.mPackageName.equals(packageName)) {
- if (deadReceivers == null) {
- deadReceivers = new ArrayList<>();
- }
- deadReceivers.add(receiver);
- }
- }
-
- // perform removal outside of mReceivers loop
- if (deadReceivers != null) {
- for (Receiver receiver : deadReceivers) {
- removeUpdates(receiver);
- }
- }
+ }, UserHandle.ALL, intentFilter, null, mLocationHandler);
}
private void onUidImportanceChanged(int uid, int importance) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
boolean foreground = isImportanceForeground(importance);
HashSet<String> affectedProviders = new HashSet<>(mRecordsByProvider.size());
- for (Entry<String, ArrayList<UpdateRecord>> entry
- : mRecordsByProvider.entrySet()) {
- String provider = entry.getKey();
- for (UpdateRecord record : entry.getValue()) {
- if (record.mReceiver.mIdentity.mUid == uid
- && record.mIsForegroundUid != foreground) {
+ synchronized (mLock) {
+ for (Entry<String, ArrayList<UpdateRecord>> entry
+ : mRecordsByProvider.entrySet()) {
+ String provider = entry.getKey();
+ for (UpdateRecord record : entry.getValue()) {
+ if (record.mReceiver.mIdentity.mUid == uid
+ && record.mIsForegroundUid != foreground) {
+ if (D) {
+ Log.d(TAG, "request from uid " + uid + " is now "
+ + (foreground ? "foreground" : "background)"));
+ }
+ record.updateForeground(foreground);
+
+ if (!isThrottlingExemptLocked(record.mReceiver.mIdentity)) {
+ affectedProviders.add(provider);
+ }
+ }
+ }
+ }
+ for (String provider : affectedProviders) {
+ applyRequirementsLocked(provider);
+ }
+
+ for (Entry<IBinder, Identity> entry : mGnssMeasurementsListeners.entrySet()) {
+ Identity callerIdentity = entry.getValue();
+ if (callerIdentity.mUid == uid) {
if (D) {
- Log.d(TAG, "request from uid " + uid + " is now "
+ Log.d(TAG, "gnss measurements listener from uid " + uid
+ + " is now " + (foreground ? "foreground" : "background)"));
+ }
+ if (foreground || isThrottlingExemptLocked(entry.getValue())) {
+ mGnssMeasurementsProvider.addListener(
+ IGnssMeasurementsListener.Stub.asInterface(entry.getKey()),
+ callerIdentity.mUid, callerIdentity.mPackageName);
+ } else {
+ mGnssMeasurementsProvider.removeListener(
+ IGnssMeasurementsListener.Stub.asInterface(entry.getKey()));
+ }
+ }
+ }
+
+ for (Entry<IBinder, Identity> entry : mGnssNavigationMessageListeners.entrySet()) {
+ Identity callerIdentity = entry.getValue();
+ if (callerIdentity.mUid == uid) {
+ if (D) {
+ Log.d(TAG, "gnss navigation message listener from uid "
+ + uid + " is now "
+ (foreground ? "foreground" : "background)"));
}
- record.updateForeground(foreground);
-
- if (!isThrottlingExemptLocked(record.mReceiver.mIdentity)) {
- affectedProviders.add(provider);
+ if (foreground || isThrottlingExemptLocked(entry.getValue())) {
+ mGnssNavigationMessageProvider.addListener(
+ IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()),
+ callerIdentity.mUid, callerIdentity.mPackageName);
+ } else {
+ mGnssNavigationMessageProvider.removeListener(
+ IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()));
}
}
}
- }
- for (String provider : affectedProviders) {
- applyRequirements(provider);
- }
- for (Entry<IBinder, Identity> entry : mGnssMeasurementsListeners.entrySet()) {
- Identity callerIdentity = entry.getValue();
- if (callerIdentity.mUid == uid) {
- if (D) {
- Log.d(TAG, "gnss measurements listener from uid " + uid
- + " is now " + (foreground ? "foreground" : "background)"));
- }
- if (foreground || isThrottlingExemptLocked(entry.getValue())) {
- mGnssMeasurementsProvider.addListener(
- IGnssMeasurementsListener.Stub.asInterface(entry.getKey()),
- callerIdentity.mUid, callerIdentity.mPackageName);
- } else {
- mGnssMeasurementsProvider.removeListener(
- IGnssMeasurementsListener.Stub.asInterface(entry.getKey()));
- }
- }
- }
-
- for (Entry<IBinder, Identity> entry : mGnssNavigationMessageListeners.entrySet()) {
- Identity callerIdentity = entry.getValue();
- if (callerIdentity.mUid == uid) {
- if (D) {
- Log.d(TAG, "gnss navigation message listener from uid "
- + uid + " is now "
- + (foreground ? "foreground" : "background)"));
- }
- if (foreground || isThrottlingExemptLocked(entry.getValue())) {
- mGnssNavigationMessageProvider.addListener(
- IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()),
- callerIdentity.mUid, callerIdentity.mPackageName);
- } else {
- mGnssNavigationMessageProvider.removeListener(
- IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()));
- }
- }
+ // TODO(b/120449926): The GNSS status listeners should be handled similar to the above.
}
}
@@ -543,33 +489,31 @@
return importance <= FOREGROUND_IMPORTANCE_CUTOFF;
}
- private void onBackgroundThrottleIntervalChanged() {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
- for (LocationProvider provider : mProviders) {
- applyRequirements(provider.getName());
- }
- }
-
- private void onBackgroundThrottleWhitelistChanged() {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
- updateBackgroundThrottlingWhitelist();
- for (LocationProvider provider : mProviders) {
- applyRequirements(provider.getName());
- }
- }
-
+ /**
+ * Makes a list of userids that are related to the current user. This is
+ * relevant when using managed profiles. Otherwise the list only contains
+ * the current user.
+ *
+ * @param currentUserId the current user, who might have an alter-ego.
+ */
private void updateUserProfiles(int currentUserId) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
- mCurrentUserProfiles = mUserManager.getProfileIdsWithDisabled(currentUserId);
+ int[] profileIds = mUserManager.getProfileIdsWithDisabled(currentUserId);
+ synchronized (mLock) {
+ mCurrentUserProfiles = profileIds;
+ }
}
+ /**
+ * Checks if the specified userId matches any of the current foreground
+ * users stored in mCurrentUserProfiles.
+ */
private boolean isCurrentProfile(int userId) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
- return ArrayUtils.contains(mCurrentUserProfiles, userId);
+ synchronized (mLock) {
+ return ArrayUtils.contains(mCurrentUserProfiles, userId);
+ }
}
private void ensureFallbackFusedProviderPresentLocked(String[] pkgs) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
PackageManager pm = mContext.getPackageManager();
String systemPackageName = mContext.getPackageName();
ArrayList<HashSet<Signature>> sigSets = ServiceWatcher.getSignatureSets(mContext, pkgs);
@@ -640,13 +584,12 @@
}
private void loadProvidersLocked() {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
// create a passive location provider, which is always enabled
LocationProvider passiveProviderManager = new LocationProvider(
LocationManager.PASSIVE_PROVIDER);
PassiveProvider passiveProvider = new PassiveProvider(passiveProviderManager);
- addProvider(passiveProviderManager);
+ addProviderLocked(passiveProviderManager);
mPassiveProvider = passiveProvider;
if (GnssLocationProvider.isSupported()) {
@@ -655,14 +598,14 @@
LocationManager.GPS_PROVIDER);
GnssLocationProvider gnssProvider = new GnssLocationProvider(mContext,
gnssProviderManager,
- mHandler.getLooper());
+ mLocationHandler.getLooper());
mGnssSystemInfoProvider = gnssProvider.getGnssSystemInfoProvider();
mGnssBatchingProvider = gnssProvider.getGnssBatchingProvider();
mGnssMetricsProvider = gnssProvider.getGnssMetricsProvider();
mGnssStatusProvider = gnssProvider.getGnssStatusProvider();
mNetInitiatedListener = gnssProvider.getNetInitiatedListener();
- addProvider(gnssProviderManager);
+ addProviderLocked(gnssProviderManager);
mRealProviders.put(LocationManager.GPS_PROVIDER, gnssProviderManager);
mGnssMeasurementsProvider = gnssProvider.getGnssMeasurementsProvider();
mGnssNavigationMessageProvider = gnssProvider.getGnssNavigationMessageProvider();
@@ -704,7 +647,7 @@
if (networkProvider != null) {
mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProviderManager);
mProxyProviders.add(networkProvider);
- addProvider(networkProviderManager);
+ addProviderLocked(networkProviderManager);
} else {
Slog.w(TAG, "no network location provider found");
}
@@ -720,7 +663,7 @@
com.android.internal.R.string.config_fusedLocationProviderPackageName,
com.android.internal.R.array.config_locationProviderPackageNames);
if (fusedProvider != null) {
- addProvider(fusedProviderManager);
+ addProviderLocked(fusedProviderManager);
mProxyProviders.add(fusedProvider);
mRealProviders.put(LocationManager.FUSED_PROVIDER, fusedProviderManager);
} else {
@@ -785,22 +728,28 @@
Boolean.parseBoolean(fragments[7]) /* supportsBearing */,
Integer.parseInt(fragments[8]) /* powerRequirement */,
Integer.parseInt(fragments[9]) /* accuracy */);
- addTestProvider(name, properties);
+ addTestProviderLocked(name, properties);
}
}
+ /**
+ * Called when the device's active user changes.
+ *
+ * @param userId the new active user's UserId
+ */
private void switchUser(int userId) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
if (mCurrentUserId == userId) {
return;
}
mBlacklist.switchUser(userId);
- mHandler.removeMessages(MSG_LOCATION_CHANGED);
- mLastLocation.clear();
- mLastLocationCoarseInterval.clear();
- updateUserProfiles(userId);
- updateProvidersSettings();
- mCurrentUserId = userId;
+ mLocationHandler.removeMessages(MSG_LOCATION_CHANGED);
+ synchronized (mLock) {
+ mLastLocation.clear();
+ mLastLocationCoarseInterval.clear();
+ updateUserProfiles(userId);
+ updateProvidersSettingsLocked();
+ mCurrentUserId = userId;
+ }
}
private static final class Identity {
@@ -859,13 +808,10 @@
}
public void setRequest(ProviderRequest request, WorkSource workSource) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
mProvider.setRequest(request, workSource);
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
-
pw.println(mName + " provider:");
pw.println(" setting=" + mSettingsEnabled);
pw.println(" enabled=" + mEnabled);
@@ -874,38 +820,34 @@
}
public long getStatusUpdateTime() {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
return mProvider.getStatusUpdateTime();
}
public int getStatus(Bundle extras) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
return mProvider.getStatus(extras);
}
public void sendExtraCommand(String command, Bundle extras) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
mProvider.sendExtraCommand(command, extras);
}
// called from any thread
@Override
public void onReportLocation(Location location) {
- // no security check necessary because this is coming from an internal-only interface
- runInternal(() -> LocationManagerService.this.handleLocationChanged(location,
+ runOnHandler(() -> LocationManagerService.this.reportLocation(location,
mProvider == mPassiveProvider));
}
// called from any thread
@Override
public void onReportLocation(List<Location> locations) {
- LocationManagerService.this.reportLocationBatch(locations);
+ runOnHandler(() -> LocationManagerService.this.reportLocationBatch(locations));
}
// called from any thread
@Override
public void onSetEnabled(boolean enabled) {
- runInternal(() -> {
+ runOnHandler(() -> {
if (enabled == mEnabled) {
return;
}
@@ -930,15 +872,17 @@
Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
"-" + LocationManager.FUSED_PROVIDER, mCurrentUserId);
- if (!enabled) {
- // If any provider has been disabled, clear all last locations for all
- // providers. This is to be on the safe side in case a provider has location
- // derived from this disabled provider.
- mLastLocation.clear();
- mLastLocationCoarseInterval.clear();
- }
+ synchronized (mLock) {
+ if (!enabled) {
+ // If any provider has been disabled, clear all last locations for all
+ // providers. This is to be on the safe side in case a provider has location
+ // derived from this disabled provider.
+ mLastLocation.clear();
+ mLastLocationCoarseInterval.clear();
+ }
- updateProviderListeners(mName);
+ updateProviderListenersLocked(mName);
+ }
mContext.sendBroadcastAsUser(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION),
UserHandle.ALL);
@@ -947,27 +891,34 @@
@Override
public void onSetProperties(ProviderProperties properties) {
- runInternal(() -> {
- mProperties = properties;
- });
+ runOnHandler(() -> mProperties = properties);
}
private void setSettingsEnabled(boolean enabled) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
- if (mSettingsEnabled == enabled) {
- return;
- }
+ synchronized (mLock) {
+ if (mSettingsEnabled == enabled) {
+ return;
+ }
- mSettingsEnabled = enabled;
- if (!mSettingsEnabled) {
- // if any provider has been disabled, clear all last locations for all
- // providers. this is to be on the safe side in case a provider has location
- // derived from this disabled provider.
- mLastLocation.clear();
- mLastLocationCoarseInterval.clear();
- updateProviderListeners(mName);
- } else if (mEnabled) {
- updateProviderListeners(mName);
+ mSettingsEnabled = enabled;
+ if (!mSettingsEnabled) {
+ // if any provider has been disabled, clear all last locations for all
+ // providers. this is to be on the safe side in case a provider has location
+ // derived from this disabled provider.
+ mLastLocation.clear();
+ mLastLocationCoarseInterval.clear();
+ updateProviderListenersLocked(mName);
+ } else if (mEnabled) {
+ updateProviderListenersLocked(mName);
+ }
+ }
+ }
+
+ private void runOnHandler(Runnable runnable) {
+ if (Looper.myLooper() == mLocationHandler.getLooper()) {
+ runnable.run();
+ } else {
+ mLocationHandler.post(runnable);
}
}
}
@@ -1071,7 +1022,7 @@
// See if receiver has any enabled update records. Also note if any update records
// are high power (has a high power provider with an interval under a threshold).
for (UpdateRecord updateRecord : mUpdateRecords.values()) {
- if (isAllowedByUserSettingsForUser(updateRecord.mProvider,
+ if (isAllowedByUserSettingsLockedForUser(updateRecord.mProvider,
mCurrentUserId)) {
requestingLocation = true;
LocationManagerService.LocationProvider locationProvider
@@ -1171,7 +1122,7 @@
synchronized (this) {
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
- mPendingIntent.send(mContext, 0, statusChanged, this, mHandler,
+ mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler,
getResolutionPermission(mAllowedResolutionLevel),
PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
// call this after broadcasting so we do not increment
@@ -1207,7 +1158,7 @@
synchronized (this) {
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
- mPendingIntent.send(mContext, 0, locationChanged, this, mHandler,
+ mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler,
getResolutionPermission(mAllowedResolutionLevel),
PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
// call this after broadcasting so we do not increment
@@ -1250,7 +1201,7 @@
synchronized (this) {
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
- mPendingIntent.send(mContext, 0, providerIntent, this, mHandler,
+ mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler,
getResolutionPermission(mAllowedResolutionLevel),
PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
// call this after broadcasting so we do not increment
@@ -1268,12 +1219,12 @@
public void binderDied() {
if (D) Log.d(TAG, "Location listener died");
- runInternal(() -> {
- removeUpdates(this);
- synchronized (this) {
- clearPendingBroadcastsLocked();
- }
- });
+ synchronized (mLock) {
+ removeUpdatesLocked(this);
+ }
+ synchronized (this) {
+ clearPendingBroadcastsLocked();
+ }
}
@Override
@@ -1310,22 +1261,28 @@
}
@Override
- public void locationCallbackFinished(ILocationListener listener) throws RemoteException {
+ public void locationCallbackFinished(ILocationListener listener) {
//Do not use getReceiverLocked here as that will add the ILocationListener to
//the receiver list if it is not found. If it is not found then the
//LocationListener was removed when it had a pending broadcast and should
//not be added back.
- runFromBinderBlocking(() -> {
+ synchronized (mLock) {
IBinder binder = listener.asBinder();
Receiver receiver = mReceivers.get(binder);
if (receiver != null) {
synchronized (receiver) {
+ // so wakelock calls will succeed
+ long identity = Binder.clearCallingIdentity();
receiver.decrementPendingBroadcastsLocked();
+ Binder.restoreCallingIdentity(identity);
}
}
- });
+ }
}
+ /**
+ * Returns the year of the GNSS hardware.
+ */
@Override
public int getGnssYearOfHardware() {
if (mGnssSystemInfoProvider != null) {
@@ -1335,6 +1292,10 @@
}
}
+
+ /**
+ * Returns the model name of the GNSS hardware.
+ */
@Override
@Nullable
public String getGnssHardwareModelName() {
@@ -1345,6 +1306,10 @@
}
}
+ /**
+ * Runs some checks for GNSS (FINE) level permissions, used by several methods which directly
+ * (try to) access GNSS information at this layer.
+ */
private boolean hasGnssPermissions(String packageName) {
int allowedResolutionLevel = getCallerAllowedResolutionLevel();
checkResolutionLevelIsSufficientForProviderUse(
@@ -1364,6 +1329,9 @@
return hasLocationAccess;
}
+ /**
+ * Returns the GNSS batching size, if available.
+ */
@Override
public int getGnssBatchSize(String packageName) {
mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1376,6 +1344,10 @@
}
}
+ /**
+ * Adds a callback for GNSS Batching events, if permissions allow, which are transported
+ * to potentially multiple listeners by the BatchedLocationCallbackTransport above this.
+ */
@Override
public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName) {
mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1419,6 +1391,9 @@
}
}
+ /**
+ * Removes callback for GNSS batching
+ */
@Override
public void removeGnssBatchingCallback() {
try {
@@ -1433,6 +1408,10 @@
mGnssBatchingDeathCallback = null;
}
+
+ /**
+ * Starts GNSS batching, if available.
+ */
@Override
public boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName) {
mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1453,6 +1432,9 @@
return mGnssBatchingProvider.start(periodNanos, wakeOnFifoFull);
}
+ /**
+ * Flushes a GNSS batch in progress
+ */
@Override
public void flushGnssBatch(String packageName) {
mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1472,6 +1454,9 @@
}
}
+ /**
+ * Stops GNSS batching
+ */
@Override
public boolean stopGnssBatch() {
mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1490,7 +1475,7 @@
checkCallerIsProvider();
// Currently used only for GNSS locations - update permissions check if changed
- if (isAllowedByUserSettingsForUser(LocationManager.GPS_PROVIDER, mCurrentUserId)) {
+ if (isAllowedByUserSettingsLockedForUser(LocationManager.GPS_PROVIDER, mCurrentUserId)) {
if (mGnssBatchingCallback == null) {
Slog.e(TAG, "reportLocationBatch() called without active Callback");
return;
@@ -1505,28 +1490,35 @@
}
}
- private void addProvider(LocationProvider provider) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+ private void addProviderLocked(LocationProvider provider) {
mProviders.add(provider);
mProvidersByName.put(provider.getName(), provider);
}
- private void removeProvider(LocationProvider provider) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+ private void removeProviderLocked(LocationProvider provider) {
mProviders.remove(provider);
mProvidersByName.remove(provider.getName());
}
- private boolean isAllowedByUserSettingsForUser(String provider, int userId) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+ /**
+ * Returns "true" if access to the specified location provider is allowed by the specified
+ * user's settings. Access to all location providers is forbidden to non-location-provider
+ * processes belonging to background users.
+ *
+ * @param provider the name of the location provider
+ * @param userId the user id to query
+ */
+ private boolean isAllowedByUserSettingsLockedForUser(String provider, int userId) {
if (LocationManager.PASSIVE_PROVIDER.equals(provider)) {
- return isLocationEnabledForUserInternal(userId);
+ return isLocationEnabledForUser(userId);
}
if (LocationManager.FUSED_PROVIDER.equals(provider)) {
- return isLocationEnabledForUserInternal(userId);
+ return isLocationEnabledForUser(userId);
}
- if (mMockProviders.containsKey(provider)) {
- return isLocationEnabledForUserInternal(userId);
+ synchronized (mLock) {
+ if (mMockProviders.containsKey(provider)) {
+ return isLocationEnabledForUser(userId);
+ }
}
long identity = Binder.clearCallingIdentity();
@@ -1541,14 +1533,29 @@
}
}
- private boolean isAllowedByUserSettings(String provider, int uid, int userId) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+
+ /**
+ * Returns "true" if access to the specified location provider is allowed by the specified
+ * user's settings. Access to all location providers is forbidden to non-location-provider
+ * processes belonging to background users.
+ *
+ * @param provider the name of the location provider
+ * @param uid the requestor's UID
+ * @param userId the user id to query
+ */
+ private boolean isAllowedByUserSettingsLocked(String provider, int uid, int userId) {
if (!isCurrentProfile(UserHandle.getUserId(uid)) && !isUidALocationProvider(uid)) {
return false;
}
- return isAllowedByUserSettingsForUser(provider, userId);
+ return isAllowedByUserSettingsLockedForUser(provider, userId);
}
+ /**
+ * Returns the permission string associated with the specified resolution level.
+ *
+ * @param resolutionLevel the resolution level
+ * @return the permission string
+ */
private String getResolutionPermission(int resolutionLevel) {
switch (resolutionLevel) {
case RESOLUTION_LEVEL_FINE:
@@ -1560,6 +1567,13 @@
}
}
+ /**
+ * Returns the resolution level allowed to the given PID/UID pair.
+ *
+ * @param pid the PID
+ * @param uid the UID
+ * @return resolution level allowed to the pid/uid pair
+ */
private int getAllowedResolutionLevel(int pid, int uid) {
if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
pid, uid) == PERMISSION_GRANTED) {
@@ -1572,16 +1586,32 @@
}
}
+ /**
+ * Returns the resolution level allowed to the caller
+ *
+ * @return resolution level allowed to caller
+ */
private int getCallerAllowedResolutionLevel() {
return getAllowedResolutionLevel(Binder.getCallingPid(), Binder.getCallingUid());
}
+ /**
+ * Throw SecurityException if specified resolution level is insufficient to use geofences.
+ *
+ * @param allowedResolutionLevel resolution level allowed to caller
+ */
private void checkResolutionLevelIsSufficientForGeofenceUse(int allowedResolutionLevel) {
if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
throw new SecurityException("Geofence usage requires ACCESS_FINE_LOCATION permission");
}
}
+ /**
+ * Return the minimum resolution level required to use the specified location provider.
+ *
+ * @param provider the name of the location provider
+ * @return minimum resolution level required for provider
+ */
private int getMinimumResolutionLevelForProviderUse(String provider) {
if (LocationManager.GPS_PROVIDER.equals(provider) ||
LocationManager.PASSIVE_PROVIDER.equals(provider)) {
@@ -1613,6 +1643,13 @@
return RESOLUTION_LEVEL_FINE; // if in doubt, require FINE
}
+ /**
+ * Throw SecurityException if specified resolution level is insufficient to use the named
+ * location provider.
+ *
+ * @param allowedResolutionLevel resolution level allowed to caller
+ * @param providerName the name of the location provider
+ */
private void checkResolutionLevelIsSufficientForProviderUse(int allowedResolutionLevel,
String providerName) {
int requiredResolutionLevel = getMinimumResolutionLevelForProviderUse(providerName);
@@ -1631,6 +1668,20 @@
}
}
+ /**
+ * Throw SecurityException if WorkSource use is not allowed (i.e. can't blame other packages
+ * for battery).
+ */
+ private void checkDeviceStatsAllowed() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.UPDATE_DEVICE_STATS, null);
+ }
+
+ private void checkUpdateAppOpsAllowed() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.UPDATE_APP_OPS_STATS, null);
+ }
+
public static int resolutionLevelToOp(int allowedResolutionLevel) {
if (allowedResolutionLevel != RESOLUTION_LEVEL_NONE) {
if (allowedResolutionLevel == RESOLUTION_LEVEL_COARSE) {
@@ -1688,15 +1739,19 @@
*/
@Override
public List<String> getAllProviders() {
- ArrayList<String> providers = new ArrayList<>(mProviders.size());
- for (LocationProvider provider : mProviders) {
- String name = provider.getName();
- if (LocationManager.FUSED_PROVIDER.equals(name)) {
- continue;
+ ArrayList<String> out;
+ synchronized (mLock) {
+ out = new ArrayList<>(mProviders.size());
+ for (LocationProvider provider : mProviders) {
+ String name = provider.getName();
+ if (LocationManager.FUSED_PROVIDER.equals(name)) {
+ continue;
+ }
+ out.add(name);
}
- providers.add(name);
}
- return providers;
+ if (D) Log.d(TAG, "getAllProviders()=" + out);
+ return out;
}
/**
@@ -1705,33 +1760,39 @@
* Can return passive provider, but never returns fused provider.
*/
@Override
- public List<String> getProviders(Criteria criteria, boolean enabledOnly)
- throws RemoteException {
+ public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
int allowedResolutionLevel = getCallerAllowedResolutionLevel();
+ ArrayList<String> out;
int uid = Binder.getCallingUid();
- return runFromBinderBlocking(() -> {
- ArrayList<String> providers = new ArrayList<>(mProviders.size());
- for (LocationProvider provider : mProviders) {
- String name = provider.getName();
- if (LocationManager.FUSED_PROVIDER.equals(name)) {
- continue;
+ long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ out = new ArrayList<>(mProviders.size());
+ for (LocationProvider provider : mProviders) {
+ String name = provider.getName();
+ if (LocationManager.FUSED_PROVIDER.equals(name)) {
+ continue;
+ }
+ if (allowedResolutionLevel >= getMinimumResolutionLevelForProviderUse(name)) {
+ if (enabledOnly
+ && !isAllowedByUserSettingsLocked(name, uid, mCurrentUserId)) {
+ continue;
+ }
+ if (criteria != null
+ && !android.location.LocationProvider.propertiesMeetCriteria(
+ name, provider.getProperties(), criteria)) {
+ continue;
+ }
+ out.add(name);
+ }
}
- if (allowedResolutionLevel < getMinimumResolutionLevelForProviderUse(name)) {
- continue;
- }
- if (enabledOnly
- && !isAllowedByUserSettings(name, uid, mCurrentUserId)) {
- continue;
- }
- if (criteria != null
- && !android.location.LocationProvider.propertiesMeetCriteria(
- name, provider.getProperties(), criteria)) {
- continue;
- }
- providers.add(name);
}
- return providers;
- });
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+
+ if (D) Log.d(TAG, "getProviders()=" + out);
+ return out;
}
/**
@@ -1742,51 +1803,59 @@
* some simplified logic.
*/
@Override
- public String getBestProvider(Criteria criteria, boolean enabledOnly) throws RemoteException {
+ public String getBestProvider(Criteria criteria, boolean enabledOnly) {
+ String result;
+
List<String> providers = getProviders(criteria, enabledOnly);
- if (providers.isEmpty()) {
- providers = getProviders(null, enabledOnly);
- }
-
if (!providers.isEmpty()) {
- if (providers.contains(LocationManager.GPS_PROVIDER)) {
- return LocationManager.GPS_PROVIDER;
- } else if (providers.contains(LocationManager.NETWORK_PROVIDER)) {
- return LocationManager.NETWORK_PROVIDER;
- } else {
- return providers.get(0);
- }
+ result = pickBest(providers);
+ if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
+ return result;
+ }
+ providers = getProviders(null, enabledOnly);
+ if (!providers.isEmpty()) {
+ result = pickBest(providers);
+ if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
+ return result;
}
+ if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + null);
return null;
}
- @Override
- public boolean providerMeetsCriteria(String provider, Criteria criteria)
- throws RemoteException {
- return runFromBinderBlocking(() -> {
- LocationProvider p = mProvidersByName.get(provider);
- if (p == null) {
- throw new IllegalArgumentException("provider=" + provider);
- }
-
- return android.location.LocationProvider.propertiesMeetCriteria(
- p.getName(), p.getProperties(), criteria);
- });
+ private String pickBest(List<String> providers) {
+ if (providers.contains(LocationManager.GPS_PROVIDER)) {
+ return LocationManager.GPS_PROVIDER;
+ } else if (providers.contains(LocationManager.NETWORK_PROVIDER)) {
+ return LocationManager.NETWORK_PROVIDER;
+ } else {
+ return providers.get(0);
+ }
}
- private void updateProvidersSettings() {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+ @Override
+ public boolean providerMeetsCriteria(String provider, Criteria criteria) {
+ LocationProvider p = mProvidersByName.get(provider);
+ if (p == null) {
+ throw new IllegalArgumentException("provider=" + provider);
+ }
+
+ boolean result = android.location.LocationProvider.propertiesMeetCriteria(
+ p.getName(), p.getProperties(), criteria);
+ if (D) Log.d(TAG, "providerMeetsCriteria(" + provider + ", " + criteria + ")=" + result);
+ return result;
+ }
+
+ private void updateProvidersSettingsLocked() {
for (LocationProvider p : mProviders) {
- p.setSettingsEnabled(isAllowedByUserSettingsForUser(p.getName(), mCurrentUserId));
+ p.setSettingsEnabled(isAllowedByUserSettingsLockedForUser(p.getName(), mCurrentUserId));
}
mContext.sendBroadcastAsUser(new Intent(LocationManager.MODE_CHANGED_ACTION),
UserHandle.ALL);
}
- private void updateProviderListeners(String provider) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+ private void updateProviderListenersLocked(String provider) {
LocationProvider p = mProvidersByName.get(provider);
if (p == null) return;
@@ -1811,15 +1880,14 @@
if (deadReceivers != null) {
for (int i = deadReceivers.size() - 1; i >= 0; i--) {
- removeUpdates(deadReceivers.get(i));
+ removeUpdatesLocked(deadReceivers.get(i));
}
}
- applyRequirements(provider);
+ applyRequirementsLocked(provider);
}
- private void applyRequirements(String provider) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+ private void applyRequirementsLocked(String provider) {
LocationProvider p = mProvidersByName.get(provider);
if (p == null) return;
@@ -1925,13 +1993,14 @@
}
@Override
- public String[] getBackgroundThrottlingWhitelist() throws RemoteException {
- return runFromBinderBlocking(
- () -> mBackgroundThrottlePackageWhitelist.toArray(new String[0]));
+ public String[] getBackgroundThrottlingWhitelist() {
+ synchronized (mLock) {
+ return mBackgroundThrottlePackageWhitelist.toArray(
+ new String[0]);
+ }
}
- private void updateBackgroundThrottlingWhitelist() {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+ private void updateBackgroundThrottlingWhitelistLocked() {
String setting = Settings.Global.getString(
mContext.getContentResolver(),
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST);
@@ -1946,8 +2015,15 @@
Arrays.asList(setting.split(",")));
}
+ private void updateLastLocationMaxAgeLocked() {
+ mLastLocationMaxAgeMs =
+ Settings.Global.getLong(
+ mContext.getContentResolver(),
+ Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
+ DEFAULT_LAST_LOCATION_MAX_AGE_MS);
+ }
+
private boolean isThrottlingExemptLocked(Identity identity) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
if (identity.mUid == Process.SYSTEM_UID) {
return true;
}
@@ -2029,7 +2105,7 @@
// and also remove the Receiver if it has no more update records
if (receiverRecords.size() == 0) {
- removeUpdates(mReceiver);
+ removeUpdatesLocked(mReceiver);
}
}
@@ -2043,9 +2119,8 @@
}
}
- private Receiver getReceiver(ILocationListener listener, int pid, int uid,
+ private Receiver getReceiverLocked(ILocationListener listener, int pid, int uid,
String packageName, WorkSource workSource, boolean hideFromAppOps) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
IBinder binder = listener.asBinder();
Receiver receiver = mReceivers.get(binder);
if (receiver == null) {
@@ -2062,9 +2137,8 @@
return receiver;
}
- private Receiver getReceiver(PendingIntent intent, int pid, int uid, String packageName,
+ private Receiver getReceiverLocked(PendingIntent intent, int pid, int uid, String packageName,
WorkSource workSource, boolean hideFromAppOps) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
Receiver receiver = mReceivers.get(intent);
if (receiver == null) {
receiver = new Receiver(null, intent, pid, uid, packageName, workSource,
@@ -2128,9 +2202,29 @@
throw new SecurityException("invalid package name: " + packageName);
}
+ private void checkPendingIntent(PendingIntent intent) {
+ if (intent == null) {
+ throw new IllegalArgumentException("invalid pending intent: " + null);
+ }
+ }
+
+ private Receiver checkListenerOrIntentLocked(ILocationListener listener, PendingIntent intent,
+ int pid, int uid, String packageName, WorkSource workSource, boolean hideFromAppOps) {
+ if (intent == null && listener == null) {
+ throw new IllegalArgumentException("need either listener or intent");
+ } else if (intent != null && listener != null) {
+ throw new IllegalArgumentException("cannot register both listener and intent");
+ } else if (intent != null) {
+ checkPendingIntent(intent);
+ return getReceiverLocked(intent, pid, uid, packageName, workSource, hideFromAppOps);
+ } else {
+ return getReceiverLocked(listener, pid, uid, packageName, workSource, hideFromAppOps);
+ }
+ }
+
@Override
public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
- PendingIntent intent, String packageName) throws RemoteException {
+ PendingIntent intent, String packageName) {
if (request == null) request = DEFAULT_LOCATION_REQUEST;
checkPackageName(packageName);
int allowedResolutionLevel = getCallerAllowedResolutionLevel();
@@ -2138,13 +2232,11 @@
request.getProvider());
WorkSource workSource = request.getWorkSource();
if (workSource != null && !workSource.isEmpty()) {
- mContext.enforceCallingOrSelfPermission(
- Manifest.permission.UPDATE_DEVICE_STATS, null);
+ checkDeviceStatsAllowed();
}
boolean hideFromAppOps = request.getHideFromAppOps();
if (hideFromAppOps) {
- mContext.enforceCallingOrSelfPermission(
- Manifest.permission.UPDATE_APP_OPS_STATS, null);
+ checkUpdateAppOpsAllowed();
}
boolean callerHasLocationHardwarePermission =
mContext.checkCallingPermission(android.Manifest.permission.LOCATION_HARDWARE)
@@ -2154,33 +2246,25 @@
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
+ // providers may use public location API's, need to clear identity
+ long identity = Binder.clearCallingIdentity();
+ try {
+ // We don't check for MODE_IGNORED here; we will do that when we go to deliver
+ // a location.
+ checkLocationAccess(pid, uid, packageName, allowedResolutionLevel);
- // We don't check for MODE_IGNORED here; we will do that when we go to deliver
- // a location.
- checkLocationAccess(pid, uid, packageName, allowedResolutionLevel);
-
- if (intent == null && listener == null) {
- throw new IllegalArgumentException("need either listener or intent");
- } else if (intent != null && listener != null) {
- throw new IllegalArgumentException("cannot register both listener and intent");
- }
-
- runFromBinderBlocking(() -> {
- Receiver receiver;
- if (intent != null) {
- receiver = getReceiver(intent, pid, uid, packageName, workSource,
- hideFromAppOps);
- } else {
- receiver = getReceiver(listener, pid, uid, packageName, workSource,
- hideFromAppOps);
+ synchronized (mLock) {
+ Receiver recevier = checkListenerOrIntentLocked(listener, intent, pid, uid,
+ packageName, workSource, hideFromAppOps);
+ requestLocationUpdatesLocked(sanitizedRequest, recevier, uid, packageName);
}
- requestLocationUpdates(sanitizedRequest, receiver, uid, packageName);
- });
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
- private void requestLocationUpdates(LocationRequest request, Receiver receiver,
+ private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver,
int uid, String packageName) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
// Figure out the provider. Either its explicitly request (legacy use cases), or
// use the fused provider
if (request == null) request = DEFAULT_LOCATION_REQUEST;
@@ -2209,7 +2293,7 @@
}
if (provider.isEnabled()) {
- applyRequirements(name);
+ applyRequirementsLocked(name);
} else {
// Notify the listener that updates are currently disabled
receiver.callProviderEnabledLocked(name, false);
@@ -2221,31 +2305,27 @@
@Override
public void removeUpdates(ILocationListener listener, PendingIntent intent,
- String packageName) throws RemoteException {
+ String packageName) {
checkPackageName(packageName);
- int pid = Binder.getCallingPid();
- int uid = Binder.getCallingUid();
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
- if (intent == null && listener == null) {
- throw new IllegalArgumentException("need either listener or intent");
- } else if (intent != null && listener != null) {
- throw new IllegalArgumentException("cannot register both listener and intent");
- }
+ synchronized (mLock) {
+ Receiver receiver = checkListenerOrIntentLocked(listener, intent, pid, uid,
+ packageName, null, false);
- runFromBinderBlocking(() -> {
- Receiver receiver;
- if (intent != null) {
- receiver = getReceiver(intent, pid, uid, packageName, null, false);
- } else {
- receiver = getReceiver(listener, pid, uid, packageName, null, false);
+ // providers may use public location API's, need to clear identity
+ long identity = Binder.clearCallingIdentity();
+ try {
+ removeUpdatesLocked(receiver);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- removeUpdates(receiver);
- });
+ }
}
- private void removeUpdates(Receiver receiver) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+ private void removeUpdatesLocked(Receiver receiver) {
if (D) Log.i(TAG, "remove " + Integer.toHexString(System.identityHashCode(receiver)));
if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
@@ -2272,14 +2352,20 @@
// update provider
for (String provider : providers) {
- applyRequirements(provider);
+ applyRequirementsLocked(provider);
+ }
+ }
+
+ private void applyAllProviderRequirementsLocked() {
+ for (LocationProvider p : mProviders) {
+ applyRequirementsLocked(p.getName());
}
}
@Override
- public Location getLastLocation(LocationRequest r, String packageName) throws RemoteException {
- if (D) Log.d(TAG, "getLastLocation: " + r);
- LocationRequest request = r != null ? r : DEFAULT_LOCATION_REQUEST;
+ public Location getLastLocation(LocationRequest request, String packageName) {
+ if (D) Log.d(TAG, "getLastLocation: " + request);
+ if (request == null) request = DEFAULT_LOCATION_REQUEST;
int allowedResolutionLevel = getCallerAllowedResolutionLevel();
checkPackageName(packageName);
checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel,
@@ -2305,60 +2391,68 @@
}
return null;
}
+
+ synchronized (mLock) {
+ // Figure out the provider. Either its explicitly request (deprecated API's),
+ // or use the fused provider
+ String name = request.getProvider();
+ if (name == null) name = LocationManager.FUSED_PROVIDER;
+ LocationProvider provider = mProvidersByName.get(name);
+ if (provider == null) return null;
+
+ if (!isAllowedByUserSettingsLocked(name, uid, mCurrentUserId)) return null;
+
+ Location location;
+ if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
+ // Make sure that an app with coarse permissions can't get frequent location
+ // updates by calling LocationManager.getLastKnownLocation repeatedly.
+ location = mLastLocationCoarseInterval.get(name);
+ } else {
+ location = mLastLocation.get(name);
+ }
+ if (location == null) {
+ return null;
+ }
+
+ // Don't return stale location to apps with foreground-only location permission.
+ String op = resolutionLevelToOpStr(allowedResolutionLevel);
+ long locationAgeMs = SystemClock.elapsedRealtime() -
+ location.getElapsedRealtimeNanos() / NANOS_PER_MILLI;
+ if ((locationAgeMs > mLastLocationMaxAgeMs)
+ && (mAppOps.unsafeCheckOp(op, uid, packageName)
+ == AppOpsManager.MODE_FOREGROUND)) {
+ return null;
+ }
+
+ if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
+ Location noGPSLocation = location.getExtraLocation(
+ Location.EXTRA_NO_GPS_LOCATION);
+ if (noGPSLocation != null) {
+ return new Location(mLocationFudger.getOrCreate(noGPSLocation));
+ }
+ } else {
+ return new Location(location);
+ }
+ }
+ return null;
} finally {
Binder.restoreCallingIdentity(identity);
}
-
- return runFromBinderBlocking(() -> {
- // Figure out the provider. Either its explicitly request (deprecated API's),
- // or use the fused provider
- String name = request.getProvider();
- if (name == null) name = LocationManager.FUSED_PROVIDER;
- LocationProvider provider = mProvidersByName.get(name);
- if (provider == null) return null;
-
- if (!isAllowedByUserSettings(name, uid, mCurrentUserId)) return null;
-
- Location location;
- if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
- // Make sure that an app with coarse permissions can't get frequent location
- // updates by calling LocationManager.getLastKnownLocation repeatedly.
- location = mLastLocationCoarseInterval.get(name);
- } else {
- location = mLastLocation.get(name);
- }
- if (location == null) {
- return null;
- }
-
- // Don't return stale location to apps with foreground-only location permission.
- String op = resolutionLevelToOpStr(allowedResolutionLevel);
- long locationAgeMs = SystemClock.elapsedRealtime()
- - location.getElapsedRealtimeNanos() / NANOS_PER_MILLI;
- if ((locationAgeMs > Settings.Global.getLong(
- mContext.getContentResolver(),
- Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
- DEFAULT_LAST_LOCATION_MAX_AGE_MS))
- && (mAppOps.unsafeCheckOp(op, uid, packageName)
- == AppOpsManager.MODE_FOREGROUND)) {
- return null;
- }
-
- if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
- Location noGPSLocation = location.getExtraLocation(
- Location.EXTRA_NO_GPS_LOCATION);
- if (noGPSLocation != null) {
- return new Location(mLocationFudger.getOrCreate(noGPSLocation));
- }
- } else {
- return new Location(location);
- }
- return null;
- });
}
+ /**
+ * Provides an interface to inject and set the last location if location is not available
+ * currently.
+ *
+ * This helps in cases where the product (Cars for example) has saved the last known location
+ * before powering off. This interface lets the client inject the saved location while the GPS
+ * chipset is getting its first fix, there by improving user experience.
+ *
+ * @param location - Location object to inject
+ * @return true if update was successful, false if not
+ */
@Override
- public boolean injectLocation(Location location) throws RemoteException {
+ public boolean injectLocation(Location location) {
mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
"Location Hardware permission not granted to inject location");
mContext.enforceCallingPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
@@ -2370,31 +2464,29 @@
}
return false;
}
-
- return runFromBinderBlocking(() -> {
- LocationProvider p = null;
- String provider = location.getProvider();
- if (provider != null) {
- p = mProvidersByName.get(provider);
+ LocationProvider p = null;
+ String provider = location.getProvider();
+ if (provider != null) {
+ p = mProvidersByName.get(provider);
+ }
+ if (p == null) {
+ if (D) {
+ Log.d(TAG, "injectLocation(): unknown provider");
}
- if (p == null) {
+ return false;
+ }
+ synchronized (mLock) {
+ if (!isAllowedByUserSettingsLockedForUser(provider, mCurrentUserId)) {
if (D) {
- Log.d(TAG, "injectLocation(): unknown provider");
- }
- return false;
- }
- if (!isAllowedByUserSettingsForUser(provider, mCurrentUserId)) {
- if (D) {
- Log.d(TAG, "Location disabled in Settings for current user:"
- + mCurrentUserId);
+ Log.d(TAG, "Location disabled in Settings for current user:" + mCurrentUserId);
}
return false;
} else {
// NOTE: If last location is already available, location is not injected. If
- // provider's normal source (like a GPS chipset) have already provided an output
+ // provider's normal source (like a GPS chipset) have already provided an output,
// there is no need to inject this location.
if (mLastLocation.get(provider) == null) {
- updateLastLocation(location, provider);
+ updateLastLocationLocked(location, provider);
} else {
if (D) {
Log.d(TAG, "injectLocation(): Location exists. Not updating");
@@ -2402,8 +2494,8 @@
return false;
}
}
- return true;
- });
+ }
+ return true;
}
@Override
@@ -2412,9 +2504,7 @@
if (request == null) request = DEFAULT_LOCATION_REQUEST;
int allowedResolutionLevel = getCallerAllowedResolutionLevel();
checkResolutionLevelIsSufficientForGeofenceUse(allowedResolutionLevel);
- if (intent == null) {
- throw new IllegalArgumentException("invalid pending intent: " + null);
- }
+ checkPendingIntent(intent);
checkPackageName(packageName);
checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel,
request.getProvider());
@@ -2425,10 +2515,7 @@
LocationRequest sanitizedRequest = createSanitizedRequest(request, allowedResolutionLevel,
callerHasLocationHardwarePermission);
- if (D) {
- Log.d(TAG, "requestGeofence: " + sanitizedRequest + " " + geofence + " "
- + intent);
- }
+ if (D) Log.d(TAG, "requestGeofence: " + sanitizedRequest + " " + geofence + " " + intent);
// geo-fence manager uses the public location API, need to clear identity
int uid = Binder.getCallingUid();
@@ -2449,9 +2536,7 @@
@Override
public void removeGeofence(Geofence geofence, PendingIntent intent, String packageName) {
- if (intent == null) {
- throw new IllegalArgumentException("invalid pending intent: " + null);
- }
+ checkPendingIntent(intent);
checkPackageName(packageName);
if (D) Log.d(TAG, "removeGeofence: " + geofence + " " + intent);
@@ -2483,14 +2568,14 @@
@Override
public boolean addGnssMeasurementsListener(
- IGnssMeasurementsListener listener, String packageName) throws RemoteException {
+ IGnssMeasurementsListener listener, String packageName) {
if (!hasGnssPermissions(packageName) || mGnssMeasurementsProvider == null) {
return false;
}
- Identity callerIdentity =
- new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
- return runFromBinderBlocking(() -> {
+ synchronized (mLock) {
+ Identity callerIdentity
+ = new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
// TODO(b/120481270): Register for client death notification and update map.
mGnssMeasurementsListeners.put(listener.asBinder(), callerIdentity);
long identity = Binder.clearCallingIdentity();
@@ -2506,7 +2591,7 @@
}
return true;
- });
+ }
}
@Override
@@ -2534,30 +2619,28 @@
}
@Override
- public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener)
- throws RemoteException {
+ public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) {
if (mGnssMeasurementsProvider == null) {
return;
}
- runFromBinderBlocking(() -> {
+ synchronized (mLock) {
mGnssMeasurementsListeners.remove(listener.asBinder());
mGnssMeasurementsProvider.removeListener(listener);
- });
+ }
}
@Override
public boolean addGnssNavigationMessageListener(
IGnssNavigationMessageListener listener,
- String packageName) throws RemoteException {
+ String packageName) {
if (!hasGnssPermissions(packageName) || mGnssNavigationMessageProvider == null) {
return false;
}
- Identity callerIdentity =
- new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
-
- return runFromBinderBlocking(() -> {
+ synchronized (mLock) {
+ Identity callerIdentity
+ = new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
// TODO(b/120481270): Register for client death notification and update map.
mGnssNavigationMessageListeners.put(listener.asBinder(), callerIdentity);
long identity = Binder.clearCallingIdentity();
@@ -2573,23 +2656,21 @@
}
return true;
- });
- }
-
- @Override
- public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener)
- throws RemoteException {
- if (mGnssNavigationMessageProvider != null) {
- runFromBinderBlocking(() -> {
- mGnssNavigationMessageListeners.remove(listener.asBinder());
- mGnssNavigationMessageProvider.removeListener(listener);
- });
}
}
@Override
- public boolean sendExtraCommand(String provider, String command, Bundle extras)
- throws RemoteException {
+ public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener) {
+ if (mGnssNavigationMessageProvider != null) {
+ synchronized (mLock) {
+ mGnssNavigationMessageListeners.remove(listener.asBinder());
+ mGnssNavigationMessageProvider.removeListener(listener);
+ }
+ }
+ }
+
+ @Override
+ public boolean sendExtraCommand(String provider, String command, Bundle extras) {
if (provider == null) {
// throw NullPointerException to remain compatible with previous implementation
throw new NullPointerException();
@@ -2603,13 +2684,13 @@
throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
}
- return runFromBinderBlocking(() -> {
+ synchronized (mLock) {
LocationProvider p = mProvidersByName.get(provider);
if (p == null) return false;
p.sendExtraCommand(command, extras);
return true;
- });
+ }
}
@Override
@@ -2626,181 +2707,251 @@
}
}
+ /**
+ * @return null if the provider does not exist
+ * @throws SecurityException if the provider is not allowed to be
+ * accessed by the caller
+ */
@Override
- public ProviderProperties getProviderProperties(String provider) throws RemoteException {
+ public ProviderProperties getProviderProperties(String provider) {
checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
provider);
- return runFromBinderBlocking(() -> {
- LocationProvider p = mProvidersByName.get(provider);
- if (p == null) return null;
- return p.getProperties();
- });
+ LocationProvider p;
+ synchronized (mLock) {
+ p = mProvidersByName.get(provider);
+ }
+
+ if (p == null) return null;
+ return p.getProperties();
}
+ /**
+ * @return null if the provider does not exist
+ * @throws SecurityException if the provider is not allowed to be
+ * accessed by the caller
+ */
@Override
- public String getNetworkProviderPackage() throws RemoteException {
- return runFromBinderBlocking(() -> {
- LocationProvider p = mProvidersByName.get(LocationManager.NETWORK_PROVIDER);
+ public String getNetworkProviderPackage() {
+ LocationProvider p;
+ synchronized (mLock) {
+ p = mProvidersByName.get(LocationManager.NETWORK_PROVIDER);
+ }
- if (p == null) {
- return null;
- }
- if (p.mProvider instanceof LocationProviderProxy) {
- return ((LocationProviderProxy) p.mProvider).getConnectedPackageName();
- }
+ if (p == null) {
return null;
- });
+ }
+ if (p.mProvider instanceof LocationProviderProxy) {
+ return ((LocationProviderProxy) p.mProvider).getConnectedPackageName();
+ }
+ return null;
}
@Override
- public void setLocationControllerExtraPackage(String packageName) throws RemoteException {
+ public void setLocationControllerExtraPackage(String packageName) {
mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
Manifest.permission.LOCATION_HARDWARE + " permission required");
-
- runFromBinderBlocking(() -> mLocationControllerExtraPackage = packageName);
+ synchronized (mLock) {
+ mLocationControllerExtraPackage = packageName;
+ }
}
@Override
- public String getLocationControllerExtraPackage() throws RemoteException {
- return runFromBinderBlocking(() -> mLocationControllerExtraPackage);
+ public String getLocationControllerExtraPackage() {
+ synchronized (mLock) {
+ return mLocationControllerExtraPackage;
+ }
}
@Override
- public void setLocationControllerExtraPackageEnabled(boolean enabled) throws RemoteException {
+ public void setLocationControllerExtraPackageEnabled(boolean enabled) {
mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
Manifest.permission.LOCATION_HARDWARE + " permission required");
- runFromBinderBlocking(() -> mLocationControllerExtraPackageEnabled = enabled);
+ synchronized (mLock) {
+ mLocationControllerExtraPackageEnabled = enabled;
+ }
}
@Override
- public boolean isLocationControllerExtraPackageEnabled() throws RemoteException {
- return runFromBinderBlocking(() -> mLocationControllerExtraPackageEnabled
- && (mLocationControllerExtraPackage != null));
+ public boolean isLocationControllerExtraPackageEnabled() {
+ synchronized (mLock) {
+ return mLocationControllerExtraPackageEnabled
+ && (mLocationControllerExtraPackage != null);
+ }
}
+ /**
+ * Returns the current location enabled/disabled status for a user
+ *
+ * @param userId the id of the user
+ * @return true if location is enabled
+ */
@Override
- public boolean isLocationEnabledForUser(int userId) throws RemoteException {
+ public boolean isLocationEnabledForUser(int userId) {
// Check INTERACT_ACROSS_USERS permission if userId is not current user id.
- if (UserHandle.getCallingUserId() != userId) {
- mContext.enforceCallingOrSelfPermission(
- Manifest.permission.INTERACT_ACROSS_USERS,
- "Requires INTERACT_ACROSS_USERS permission");
- }
+ checkInteractAcrossUsersPermission(userId);
- return runFromBinderBlocking(() -> isLocationEnabledForUserInternal(userId));
+ long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ final String allowedProviders = Settings.Secure.getStringForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+ userId);
+ if (allowedProviders == null) {
+ return false;
+ }
+ final List<String> providerList = Arrays.asList(allowedProviders.split(","));
+ for (String provider : mRealProviders.keySet()) {
+ if (provider.equals(LocationManager.PASSIVE_PROVIDER)
+ || provider.equals(LocationManager.FUSED_PROVIDER)) {
+ continue;
+ }
+ if (providerList.contains(provider)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
- private boolean isLocationEnabledForUserInternal(int userId) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
-
- final String allowedProviders = Settings.Secure.getStringForUser(
- mContext.getContentResolver(),
- Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
- userId);
- if (allowedProviders == null) {
- return false;
- }
- final List<String> providerList = Arrays.asList(allowedProviders.split(","));
-
- for (String provider : mRealProviders.keySet()) {
- if (provider.equals(LocationManager.PASSIVE_PROVIDER)
- || provider.equals(LocationManager.FUSED_PROVIDER)) {
- continue;
- }
- if (providerList.contains(provider)) {
- return true;
- }
- }
- return false;
- }
-
+ /**
+ * Enable or disable location for a user
+ *
+ * @param enabled true to enable location, false to disable location
+ * @param userId the id of the user
+ */
@Override
- public void setLocationEnabledForUser(boolean enabled, int userId) throws RemoteException {
+ public void setLocationEnabledForUser(boolean enabled, int userId) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.WRITE_SECURE_SETTINGS,
"Requires WRITE_SECURE_SETTINGS permission");
// Check INTERACT_ACROSS_USERS permission if userId is not current user id.
- if (UserHandle.getCallingUserId() != userId) {
- mContext.enforceCallingOrSelfPermission(
- Manifest.permission.INTERACT_ACROSS_USERS,
- "Requires INTERACT_ACROSS_USERS permission");
- }
+ checkInteractAcrossUsersPermission(userId);
- runFromBinderBlocking(() -> {
- final Set<String> allRealProviders = mRealProviders.keySet();
- // Update all providers on device plus gps and network provider when disabling
- // location
- Set<String> allProvidersSet = new ArraySet<>(allRealProviders.size() + 2);
- allProvidersSet.addAll(allRealProviders);
- // When disabling location, disable gps and network provider that could have been
- // enabled by location mode api.
- if (!enabled) {
- allProvidersSet.add(LocationManager.GPS_PROVIDER);
- allProvidersSet.add(LocationManager.NETWORK_PROVIDER);
- }
- if (allProvidersSet.isEmpty()) {
- return;
- }
- // to ensure thread safety, we write the provider name with a '+' or '-'
- // and let the SettingsProvider handle it rather than reading and modifying
- // the list of enabled providers.
- final String prefix = enabled ? "+" : "-";
- StringBuilder locationProvidersAllowed = new StringBuilder();
- for (String provider : allProvidersSet) {
- if (provider.equals(LocationManager.PASSIVE_PROVIDER)
- || provider.equals(LocationManager.FUSED_PROVIDER)) {
- continue;
+ long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ final Set<String> allRealProviders = mRealProviders.keySet();
+ // Update all providers on device plus gps and network provider when disabling
+ // location
+ Set<String> allProvidersSet = new ArraySet<>(allRealProviders.size() + 2);
+ allProvidersSet.addAll(allRealProviders);
+ // When disabling location, disable gps and network provider that could have been
+ // enabled by location mode api.
+ if (!enabled) {
+ allProvidersSet.add(LocationManager.GPS_PROVIDER);
+ allProvidersSet.add(LocationManager.NETWORK_PROVIDER);
}
- locationProvidersAllowed.append(prefix);
- locationProvidersAllowed.append(provider);
- locationProvidersAllowed.append(",");
+ if (allProvidersSet.isEmpty()) {
+ return;
+ }
+ // to ensure thread safety, we write the provider name with a '+' or '-'
+ // and let the SettingsProvider handle it rather than reading and modifying
+ // the list of enabled providers.
+ final String prefix = enabled ? "+" : "-";
+ StringBuilder locationProvidersAllowed = new StringBuilder();
+ for (String provider : allProvidersSet) {
+ if (provider.equals(LocationManager.PASSIVE_PROVIDER)
+ || provider.equals(LocationManager.FUSED_PROVIDER)) {
+ continue;
+ }
+ locationProvidersAllowed.append(prefix);
+ locationProvidersAllowed.append(provider);
+ locationProvidersAllowed.append(",");
+ }
+ // Remove the trailing comma
+ locationProvidersAllowed.setLength(locationProvidersAllowed.length() - 1);
+ Settings.Secure.putStringForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+ locationProvidersAllowed.toString(),
+ userId);
}
- // Remove the trailing comma
- locationProvidersAllowed.setLength(locationProvidersAllowed.length() - 1);
- Settings.Secure.putStringForUser(
- mContext.getContentResolver(),
- Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
- locationProvidersAllowed.toString(),
- userId);
- });
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
+ /**
+ * Returns the current enabled/disabled status of a location provider and user
+ *
+ * @param providerName name of the provider
+ * @param userId the id of the user
+ * @return true if the provider exists and is enabled
+ */
@Override
- public boolean isProviderEnabledForUser(String providerName, int userId)
- throws RemoteException {
+ public boolean isProviderEnabledForUser(String providerName, int userId) {
// Check INTERACT_ACROSS_USERS permission if userId is not current user id.
- if (UserHandle.getCallingUserId() != userId) {
- mContext.enforceCallingOrSelfPermission(
- Manifest.permission.INTERACT_ACROSS_USERS,
- "Requires INTERACT_ACROSS_USERS permission");
+ checkInteractAcrossUsersPermission(userId);
+
+ if (!isLocationEnabledForUser(userId)) {
+ return false;
}
// Fused provider is accessed indirectly via criteria rather than the provider-based APIs,
// so we discourage its use
if (LocationManager.FUSED_PROVIDER.equals(providerName)) return false;
- if (!isLocationEnabledForUser(userId)) {
- return false;
- }
-
- return runFromBinderBlocking(() -> {
- LocationProvider provider = mProvidersByName.get(providerName);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ LocationProvider provider;
+ synchronized (mLock) {
+ provider = mProvidersByName.get(providerName);
+ }
return provider != null && provider.isEnabled();
- });
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
+ /**
+ * Enable or disable a single location provider.
+ *
+ * @param provider name of the provider
+ * @param enabled true to enable the provider. False to disable the provider
+ * @param userId the id of the user to set
+ * @return true if the value was set, false on errors
+ */
@Override
public boolean setProviderEnabledForUser(String provider, boolean enabled, int userId) {
return false;
}
+ /**
+ * Method for checking INTERACT_ACROSS_USERS permission if specified user id is not the same as
+ * current user id
+ *
+ * @param userId the user id to get or set value
+ */
+ private void checkInteractAcrossUsersPermission(int userId) {
+ int uid = Binder.getCallingUid();
+ if (UserHandle.getUserId(uid) != userId) {
+ if (ActivityManager.checkComponentPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS, uid, -1, true)
+ != PERMISSION_GRANTED) {
+ throw new SecurityException("Requires INTERACT_ACROSS_USERS permission");
+ }
+ }
+ }
+
+ /**
+ * Returns "true" if the UID belongs to a bound location provider.
+ *
+ * @param uid the uid
+ * @return true if uid belongs to a bound location provider
+ */
private boolean isUidALocationProvider(int uid) {
if (uid == Process.SYSTEM_UID) {
return true;
}
-
+ if (mGeocodeProvider != null) {
+ if (doesUidHavePackage(uid, mGeocodeProvider.getConnectedPackageName())) return true;
+ }
for (LocationProviderProxy proxy : mProxyProviders) {
if (doesUidHavePackage(uid, proxy.getConnectedPackageName())) return true;
}
@@ -2819,6 +2970,7 @@
// providers installed oustide the system image. So
// also allow providers with a UID matching the
// currently bound package name
+
if (isUidALocationProvider(Binder.getCallingUid())) {
return;
}
@@ -2827,6 +2979,9 @@
"or UID of a currently bound location provider");
}
+ /**
+ * Returns true if the given package belongs to the given uid.
+ */
private boolean doesUidHavePackage(int uid, String packageName) {
if (packageName == null) {
return false;
@@ -2843,12 +2998,22 @@
return false;
}
- // TODO: will be removed in future
@Override
public void reportLocation(Location location, boolean passive) {
- throw new IllegalStateException("operation unsupported");
+ checkCallerIsProvider();
+
+ if (!location.isComplete()) {
+ Log.w(TAG, "Dropping incomplete location: " + location);
+ return;
+ }
+
+ mLocationHandler.removeMessages(MSG_LOCATION_CHANGED, location);
+ Message m = Message.obtain(mLocationHandler, MSG_LOCATION_CHANGED, location);
+ m.arg1 = (passive ? 1 : 0);
+ mLocationHandler.sendMessageAtFrontOfQueue(m);
}
+
private static boolean shouldBroadcastSafe(
Location loc, Location lastLoc, UpdateRecord record, long now) {
// Always broadcast the first update
@@ -2881,33 +3046,14 @@
return record.mRealRequest.getExpireAt() >= now;
}
- private void handleLocationChanged(Location location, boolean passive) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
-
- // create a working copy of the incoming Location so that the service can modify it without
- // disturbing the caller's copy
- Location myLocation = new Location(location);
- String pr = myLocation.getProvider();
-
- // set "isFromMockProvider" bit if location came from a mock provider. we do not clear this
- // bit if location did not come from a mock provider because passive/fused providers can
- // forward locations from mock providers, and should not grant them legitimacy in doing so.
- if (!myLocation.isFromMockProvider() && isMockProvider(pr)) {
- myLocation.setIsFromMockProvider(true);
- }
-
- if (!passive) {
- // notify passive provider of the new location
- mPassiveProvider.updateLocation(myLocation);
- }
-
- if (D) Log.d(TAG, "incoming location: " + myLocation);
+ private void handleLocationChangedLocked(Location location, boolean passive) {
+ if (D) Log.d(TAG, "incoming location: " + location);
long now = SystemClock.elapsedRealtime();
- String provider = (passive ? LocationManager.PASSIVE_PROVIDER : myLocation.getProvider());
+ String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider());
// Skip if the provider is unknown.
LocationProvider p = mProvidersByName.get(provider);
if (p == null) return;
- updateLastLocation(myLocation, provider);
+ updateLastLocationLocked(location, provider);
// mLastLocation should have been updated from the updateLastLocationLocked call above.
Location lastLocation = mLastLocation.get(provider);
if (lastLocation == null) {
@@ -2918,13 +3064,13 @@
// Update last known coarse interval location if enough time has passed.
Location lastLocationCoarseInterval = mLastLocationCoarseInterval.get(provider);
if (lastLocationCoarseInterval == null) {
- lastLocationCoarseInterval = new Location(myLocation);
+ lastLocationCoarseInterval = new Location(location);
mLastLocationCoarseInterval.put(provider, lastLocationCoarseInterval);
}
- long timeDiffNanos = myLocation.getElapsedRealtimeNanos()
+ long timeDiffNanos = location.getElapsedRealtimeNanos()
- lastLocationCoarseInterval.getElapsedRealtimeNanos();
if (timeDiffNanos > LocationFudger.FASTEST_INTERVAL_MS * NANOS_PER_MILLI) {
- lastLocationCoarseInterval.set(myLocation);
+ lastLocationCoarseInterval.set(location);
}
// Don't ever return a coarse location that is more recent than the allowed update
// interval (i.e. don't allow an app to keep registering and unregistering for
@@ -3048,20 +3194,24 @@
// remove dead records and receivers outside the loop
if (deadReceivers != null) {
for (Receiver receiver : deadReceivers) {
- removeUpdates(receiver);
+ removeUpdatesLocked(receiver);
}
}
if (deadUpdateRecords != null) {
for (UpdateRecord r : deadUpdateRecords) {
r.disposeLocked(true);
}
- applyRequirements(provider);
+ applyRequirementsLocked(provider);
}
}
- private void updateLastLocation(Location location, String provider) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
-
+ /**
+ * Updates last location with the given location
+ *
+ * @param location new location to update
+ * @param provider Location provider to update for
+ */
+ private void updateLastLocationLocked(Location location, String provider) {
Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
Location lastNoGPSLocation;
Location lastLocation = mLastLocation.get(provider);
@@ -3079,11 +3229,75 @@
lastLocation.set(location);
}
- private boolean isMockProvider(String provider) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
- return mMockProviders.containsKey(provider);
+ private class LocationWorkerHandler extends Handler {
+ public LocationWorkerHandler(Looper looper) {
+ super(looper, null, true);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_LOCATION_CHANGED:
+ handleLocationChanged((Location) msg.obj, msg.arg1 == 1);
+ break;
+ }
+ }
}
+ private boolean isMockProvider(String provider) {
+ synchronized (mLock) {
+ return mMockProviders.containsKey(provider);
+ }
+ }
+
+ private void handleLocationChanged(Location location, boolean passive) {
+ // create a working copy of the incoming Location so that the service can modify it without
+ // disturbing the caller's copy
+ Location myLocation = new Location(location);
+ String provider = myLocation.getProvider();
+
+ // set "isFromMockProvider" bit if location came from a mock provider. we do not clear this
+ // bit if location did not come from a mock provider because passive/fused providers can
+ // forward locations from mock providers, and should not grant them legitimacy in doing so.
+ if (!myLocation.isFromMockProvider() && isMockProvider(provider)) {
+ myLocation.setIsFromMockProvider(true);
+ }
+
+ synchronized (mLock) {
+ if (!passive) {
+ // notify passive provider of the new location
+ mPassiveProvider.updateLocation(myLocation);
+ }
+ handleLocationChangedLocked(myLocation, passive);
+ }
+ }
+
+ private final PackageMonitor mPackageMonitor = new PackageMonitor() {
+ @Override
+ public void onPackageDisappeared(String packageName, int reason) {
+ // remove all receivers associated with this package name
+ synchronized (mLock) {
+ ArrayList<Receiver> deadReceivers = null;
+
+ for (Receiver receiver : mReceivers.values()) {
+ if (receiver.mIdentity.mPackageName.equals(packageName)) {
+ if (deadReceivers == null) {
+ deadReceivers = new ArrayList<>();
+ }
+ deadReceivers.add(receiver);
+ }
+ }
+
+ // perform removal outside of mReceivers loop
+ if (deadReceivers != null) {
+ for (Receiver receiver : deadReceivers) {
+ removeUpdatesLocked(receiver);
+ }
+ }
+ }
+ }
+ };
+
// Geocoder
@Override
@@ -3124,8 +3338,7 @@
}
@Override
- public void addTestProvider(String name, ProviderProperties properties, String opPackageName)
- throws RemoteException {
+ public void addTestProvider(String name, ProviderProperties properties, String opPackageName) {
if (!canCallerAccessMockLocation(opPackageName)) {
return;
}
@@ -3134,24 +3347,23 @@
throw new IllegalArgumentException("Cannot mock the passive location provider");
}
- runFromBinderBlocking(() -> {
+ long identity = Binder.clearCallingIdentity();
+ synchronized (mLock) {
// remove the real provider if we are replacing GPS or network provider
if (LocationManager.GPS_PROVIDER.equals(name)
|| LocationManager.NETWORK_PROVIDER.equals(name)
|| LocationManager.FUSED_PROVIDER.equals(name)) {
LocationProvider p = mProvidersByName.get(name);
if (p != null) {
- removeProvider(p);
+ removeProviderLocked(p);
}
}
- addTestProvider(name, properties);
- return null;
- });
+ addTestProviderLocked(name, properties);
+ }
+ Binder.restoreCallingIdentity(identity);
}
- private void addTestProvider(String name, ProviderProperties properties) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
-
+ private void addTestProviderLocked(String name, ProviderProperties properties) {
if (mProvidersByName.get(name) != null) {
throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
}
@@ -3159,103 +3371,122 @@
LocationProvider provider = new LocationProvider(name);
MockProvider mockProvider = new MockProvider(provider, properties);
- addProvider(provider);
+ addProviderLocked(provider);
mMockProviders.put(name, mockProvider);
mLastLocation.put(name, null);
mLastLocationCoarseInterval.put(name, null);
}
@Override
- public void removeTestProvider(String provider, String opPackageName) throws RemoteException {
+ public void removeTestProvider(String provider, String opPackageName) {
if (!canCallerAccessMockLocation(opPackageName)) {
return;
}
- runFromBinderBlocking(() -> {
+ synchronized (mLock) {
MockProvider mockProvider = mMockProviders.remove(provider);
if (mockProvider == null) {
throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
}
- removeProvider(mProvidersByName.get(provider));
+ long identity = Binder.clearCallingIdentity();
+ try {
+ removeProviderLocked(mProvidersByName.get(provider));
- // reinstate real provider if available
- LocationProvider realProvider = mRealProviders.get(provider);
- if (realProvider != null) {
- addProvider(realProvider);
+ // reinstate real provider if available
+ LocationProvider realProvider = mRealProviders.get(provider);
+ if (realProvider != null) {
+ addProviderLocked(realProvider);
+ }
+ mLastLocation.put(provider, null);
+ mLastLocationCoarseInterval.put(provider, null);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- mLastLocation.put(provider, null);
- mLastLocationCoarseInterval.put(provider, null);
- });
+ }
}
@Override
- public void setTestProviderLocation(String provider, Location loc, String opPackageName)
- throws RemoteException {
+ public void setTestProviderLocation(String provider, Location loc, String opPackageName) {
if (!canCallerAccessMockLocation(opPackageName)) {
return;
}
- runFromBinderBlocking(() -> {
+ synchronized (mLock) {
MockProvider mockProvider = mMockProviders.get(provider);
if (mockProvider == null) {
throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
}
- // Ensure that the location is marked as being mock. There's some logic to do this
- // in handleLocationChanged(), but it fails if loc has the wrong provider
- // (b/33091107).
+ // Ensure that the location is marked as being mock. There's some logic to do this in
+ // handleLocationChanged(), but it fails if loc has the wrong provider (bug 33091107).
Location mock = new Location(loc);
mock.setIsFromMockProvider(true);
if (!TextUtils.isEmpty(loc.getProvider()) && !provider.equals(loc.getProvider())) {
- // The location has an explicit provider that is different from the mock
- // provider name. The caller may be trying to fool us via bug 33091107.
+ // The location has an explicit provider that is different from the mock provider
+ // name. The caller may be trying to fool us via bug 33091107.
EventLog.writeEvent(0x534e4554, "33091107", Binder.getCallingUid(),
provider + "!=" + loc.getProvider());
}
- mockProvider.setLocation(mock);
- });
+ // clear calling identity so INSTALL_LOCATION_PROVIDER permission is not required
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mockProvider.setLocation(mock);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
}
@Override
- public void setTestProviderEnabled(String provider, boolean enabled, String opPackageName)
- throws RemoteException {
+ public void setTestProviderEnabled(String provider, boolean enabled, String opPackageName) {
if (!canCallerAccessMockLocation(opPackageName)) {
return;
}
- runFromBinderBlocking(() -> {
+ synchronized (mLock) {
MockProvider mockProvider = mMockProviders.get(provider);
if (mockProvider == null) {
throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
}
- mockProvider.setEnabled(enabled);
- });
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mockProvider.setEnabled(enabled);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
}
@Override
public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime,
- String opPackageName) throws RemoteException {
+ String opPackageName) {
if (!canCallerAccessMockLocation(opPackageName)) {
return;
}
- runFromBinderBlocking(() -> {
+ synchronized (mLock) {
MockProvider mockProvider = mMockProviders.get(provider);
if (mockProvider == null) {
throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
}
mockProvider.setStatus(status, extras, updateTime);
- });
+ }
+ }
+
+ private void log(String log) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Slog.d(TAG, log);
+ }
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
- Runnable dump = () -> {
+ synchronized (mLock) {
if (args.length > 0 && args[0].equals("--gnssmetrics")) {
if (mGnssMetricsProvider != null) {
pw.append(mGnssMetricsProvider.getGnssMetricsAsProtoString());
@@ -3348,14 +3579,6 @@
if (mGnssBatchingInProgress) {
pw.println(" GNSS batching in progress");
}
- };
-
- FutureTask<Void> task = new FutureTask<>(dump, null);
- mHandler.postAtFrontOfQueue(task);
- try {
- task.get();
- } catch (InterruptedException | ExecutionException e) {
- pw.println("error dumping: " + e);
}
}
}
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 9f353a8..5d4c5c3 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -149,6 +149,8 @@
native static boolean vibratorSupportsAmplitudeControl();
native static void vibratorSetAmplitude(int amplitude);
native static long vibratorPerformEffect(long effect, long strength);
+ static native boolean vibratorSupportsExternalControl();
+ static native void vibratorSetExternalControl(boolean enabled);
private final IUidObserver mUidObserver = new IUidObserver.Stub() {
@Override public void onUidStateChanged(int uid, int procState, long procStateSeq) {
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index f9a77af..d7cb2bd 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -68,6 +68,7 @@
sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYER_APP, String.class);
sGlobalSettingToTypeMap.put(Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED, int.class);
sGlobalSettingToTypeMap.put(Settings.Global.GUP_DEV_OPT_IN_APPS, String.class);
+ sGlobalSettingToTypeMap.put(Settings.Global.GUP_DEV_OPT_OUT_APPS, String.class);
sGlobalSettingToTypeMap.put(Settings.Global.GUP_BLACK_LIST, String.class);
// add other global settings here...
}
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index a3ebe24..8e26097 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -274,10 +274,14 @@
}
synchronized (mLock) {
- final S service = getServiceForUserLocked(userId);
- if (service != null) {
- service.setTemporaryServiceLocked(componentName, durationMs);
+ final S oldService = peekServiceForUserLocked(userId);
+ if (oldService != null) {
+ oldService.removeSelfFromCacheLocked();
}
+ mServiceNameResolver.setTemporaryService(userId, componentName, durationMs);
+
+ // Must update the service on cache so its initialization code is triggered
+ updateCachedServiceLocked(userId);
}
}
@@ -500,6 +504,7 @@
protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
boolean realDebug = debug;
boolean realVerbose = verbose;
+ final String prefix2 = " ";
try {
// Temporarily turn on full logging;
@@ -510,6 +515,13 @@
if (mServiceNameResolver != null) {
pw.print(prefix); pw.print("Name resolver: ");
mServiceNameResolver.dumpShort(pw); pw.println();
+ final UserManager um = getContext().getSystemService(UserManager.class);
+ final List<UserInfo> users = um.getUsers();
+ for (int i = 0; i < users.size(); i++) {
+ final int userId = users.get(i).id;
+ pw.print(prefix2); pw.print(userId); pw.print(": ");
+ mServiceNameResolver.dumpShort(pw, userId); pw.println();
+ }
}
pw.print(prefix); pw.print("Disabled users: "); pw.println(mDisabledUsers);
pw.print(prefix); pw.print("Allow instant service: "); pw.println(mAllowInstantService);
@@ -522,7 +534,6 @@
pw.println("none");
} else {
pw.println(size);
- final String prefix2 = " ";
for (int i = 0; i < size; i++) {
pw.print(prefix); pw.print("Service at "); pw.print(i); pw.println(": ");
final S service = mServicesCache.valueAt(i);
diff --git a/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
index 556e489..cd9ebcd 100644
--- a/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
@@ -214,7 +214,7 @@
/**
* Gets the current name of the service, which is either the default service or the
- * {@link #setTemporaryServiceLocked(String, int) temporary one}.
+ * {@link AbstractMasterSystemService#setTemporaryService(int, String, int) temporary one}.
*/
protected final @Nullable String getComponentNameLocked() {
return mMaster.mServiceNameResolver.getServiceName(mUserId);
@@ -228,17 +228,6 @@
}
/**
- * Temporary sets the service implementation.
- *
- * @param componentName name of the new component
- * @param durationMs how long the change will be valid (the service will be automatically reset
- * to the default component after this timeout expires).
- */
- protected final void setTemporaryServiceLocked(@NonNull String componentName, int durationMs) {
- mMaster.mServiceNameResolver.setTemporaryService(mUserId, componentName, durationMs);
- }
-
- /**
* Resets the temporary service implementation to the default component.
*/
protected final void resetTemporaryServiceLocked() {
diff --git a/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java b/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
index 7027246..7f198ac 100644
--- a/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
+++ b/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
@@ -159,7 +159,7 @@
@Override
public String toString() {
- return "FrameworkResourcesServiceNamer: temps=" + mTemporaryServiceNames;
+ return "FrameworkResourcesServiceNamer[temps=" + mTemporaryServiceNames + "]";
}
// TODO(b/117779333): support proto
diff --git a/services/core/java/com/android/server/infra/SecureSettingsServiceNameResolver.java b/services/core/java/com/android/server/infra/SecureSettingsServiceNameResolver.java
index bcaa918..cac7f53 100644
--- a/services/core/java/com/android/server/infra/SecureSettingsServiceNameResolver.java
+++ b/services/core/java/com/android/server/infra/SecureSettingsServiceNameResolver.java
@@ -56,4 +56,9 @@
public void dumpShort(@NonNull PrintWriter pw, @UserIdInt int userId) {
pw.print("defaultService="); pw.print(getDefaultServiceName(userId));
}
+
+ @Override
+ public String toString() {
+ return "SecureSettingsServiceNameResolver[" + mProperty + "]";
+ }
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index d96b6cb..e7c3c7b 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -1951,6 +1951,11 @@
}
// Native callback.
+ private int getPointerDisplayId() {
+ return mWindowManagerCallbacks.getPointerDisplayId();
+ }
+
+ // Native callback.
private String[] getKeyboardLayoutOverlay(InputDeviceIdentifier identifier) {
if (!mSystemReady) {
return null;
@@ -2017,6 +2022,8 @@
KeyEvent event, int policyFlags);
public int getPointerLayer();
+
+ public int getPointerDisplayId();
}
/**
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index d45869e..840c6e4 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1674,40 +1674,93 @@
@Override
public List<InputMethodInfo> getInputMethodList() {
- return getInputMethodList(false /* isVrOnly */);
+ final int callingUserId = UserHandle.getCallingUserId();
+ synchronized (mMethodMap) {
+ final int[] resolvedUserIds = InputMethodUtils.resolveUserId(callingUserId,
+ mSettings.getCurrentUserId(), null);
+ if (resolvedUserIds.length != 1) {
+ return Collections.emptyList();
+ }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return getInputMethodListLocked(false /* isVrOnly */, resolvedUserIds[0]);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
}
@Override
public List<InputMethodInfo> getVrInputMethodList() {
- return getInputMethodList(true /* isVrOnly */);
- }
-
- private List<InputMethodInfo> getInputMethodList(final boolean isVrOnly) {
+ final int callingUserId = UserHandle.getCallingUserId();
synchronized (mMethodMap) {
- // TODO: Make this work even for non-current users?
- if (!calledFromValidUserLocked()) {
+ final int[] resolvedUserIds = InputMethodUtils.resolveUserId(callingUserId,
+ mSettings.getCurrentUserId(), null);
+ if (resolvedUserIds.length != 1) {
return Collections.emptyList();
}
- ArrayList<InputMethodInfo> methodList = new ArrayList<>();
- for (InputMethodInfo info : mMethodList) {
-
- if (info.isVrOnly() == isVrOnly) {
- methodList.add(info);
- }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return getInputMethodListLocked(true /* isVrOnly */, resolvedUserIds[0]);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
- return methodList;
}
}
@Override
public List<InputMethodInfo> getEnabledInputMethodList() {
+ final int callingUserId = UserHandle.getCallingUserId();
synchronized (mMethodMap) {
- // TODO: Make this work even for non-current users?
- if (!calledFromValidUserLocked()) {
+ final int[] resolvedUserIds = InputMethodUtils.resolveUserId(callingUserId,
+ mSettings.getCurrentUserId(), null);
+ if (resolvedUserIds.length != 1) {
return Collections.emptyList();
}
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return getEnabledInputMethodListLocked(resolvedUserIds[0]);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ @GuardedBy("mMethodMap")
+ private List<InputMethodInfo> getInputMethodListLocked(boolean isVrOnly,
+ @UserIdInt int userId) {
+ final ArrayList<InputMethodInfo> methodList;
+ if (userId == mSettings.getCurrentUserId()) {
+ // Create a copy.
+ methodList = new ArrayList<>(mMethodList);
+ } else {
+ final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+ methodList = new ArrayList<>();
+ final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
+ new ArrayMap<>();
+ AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
+ queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
+ methodList);
+ }
+ methodList.removeIf(imi -> imi.isVrOnly() != isVrOnly);
+ return methodList;
+ }
+
+ @GuardedBy("mMethodMap")
+ private List<InputMethodInfo> getEnabledInputMethodListLocked(@UserIdInt int userId) {
+ if (userId == mSettings.getCurrentUserId()) {
return mSettings.getEnabledInputMethodListLocked();
}
+ final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+ final ArrayList<InputMethodInfo> methodList = new ArrayList<>();
+ final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
+ new ArrayMap<>();
+ AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
+ queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
+ methodList);
+ final InputMethodSettings settings = new InputMethodSettings(mContext.getResources(),
+ mContext.getContentResolver(), methodMap, methodList, userId, true);
+ return settings.getEnabledInputMethodListLocked();
}
/**
@@ -1717,11 +1770,27 @@
@Override
public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId,
boolean allowsImplicitlySelectedSubtypes) {
+ final int callingUserId = UserHandle.getCallingUserId();
synchronized (mMethodMap) {
- // TODO: Make this work even for non-current users?
- if (!calledFromValidUserLocked()) {
+ final int[] resolvedUserIds = InputMethodUtils.resolveUserId(callingUserId,
+ mSettings.getCurrentUserId(), null);
+ if (resolvedUserIds.length != 1) {
return Collections.emptyList();
}
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return getEnabledInputMethodSubtypeListLocked(imiId,
+ allowsImplicitlySelectedSubtypes, resolvedUserIds[0]);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ @GuardedBy("mMethodMap")
+ private List<InputMethodSubtype> getEnabledInputMethodSubtypeListLocked(String imiId,
+ boolean allowsImplicitlySelectedSubtypes, @UserIdInt int userId) {
+ if (userId == mSettings.getCurrentUserId()) {
final InputMethodInfo imi;
if (imiId == null && mCurMethodId != null) {
imi = mMethodMap.get(mCurMethodId);
@@ -1734,6 +1803,21 @@
return mSettings.getEnabledInputMethodSubtypeListLocked(
mContext, imi, allowsImplicitlySelectedSubtypes);
}
+ final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+ final ArrayList<InputMethodInfo> methodList = new ArrayList<>();
+ final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
+ new ArrayMap<>();
+ AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
+ queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
+ methodList);
+ final InputMethodInfo imi = methodMap.get(imiId);
+ if (imi == null) {
+ return Collections.emptyList();
+ }
+ final InputMethodSettings settings = new InputMethodSettings(mContext.getResources(),
+ mContext.getContentResolver(), methodMap, methodList, userId, true);
+ return settings.getEnabledInputMethodSubtypeListLocked(
+ mContext, imi, allowsImplicitlySelectedSubtypes);
}
/**
@@ -4545,6 +4629,7 @@
private int handleShellCommandListInputMethods(@NonNull ShellCommand shellCommand) {
boolean all = false;
boolean brief = false;
+ int userIdToBeResolved = UserHandle.USER_CURRENT;
while (true) {
final String nextOption = shellCommand.getNextOption();
if (nextOption == null) {
@@ -4557,19 +4642,34 @@
case "-s":
brief = true;
break;
+ case "-u":
+ case "--user":
+ userIdToBeResolved = UserHandle.parseUserArg(shellCommand.getNextArgRequired());
+ break;
}
}
- final List<InputMethodInfo> methods = all ?
- getInputMethodList() : getEnabledInputMethodList();
- final PrintWriter pr = shellCommand.getOutPrintWriter();
- final Printer printer = x -> pr.println(x);
- final int N = methods.size();
- for (int i = 0; i < N; ++i) {
- if (brief) {
- pr.println(methods.get(i).getId());
- } else {
- pr.print(methods.get(i).getId()); pr.println(":");
- methods.get(i).dump(printer, " ");
+ synchronized (mMethodMap) {
+ final PrintWriter pr = shellCommand.getOutPrintWriter();
+ final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
+ mSettings.getCurrentUserId(), shellCommand.getErrPrintWriter());
+ for (int userId : userIds) {
+ final List<InputMethodInfo> methods = all
+ ? getInputMethodListLocked(false, userId)
+ : getEnabledInputMethodListLocked(userId);
+ if (userIds.length > 1) {
+ pr.print("User #");
+ pr.print(userId);
+ pr.println(":");
+ }
+ for (InputMethodInfo info : methods) {
+ if (brief) {
+ pr.println(info.getId());
+ } else {
+ pr.print(info.getId());
+ pr.println(":");
+ info.dump(pr::println, " ");
+ }
+ }
}
}
return ShellCommandResult.SUCCESS;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index 1137bf9..2f76871 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -29,9 +29,12 @@
import android.os.Build;
import android.os.LocaleList;
import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManagerInternal;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.IntArray;
import android.util.Pair;
import android.util.Printer;
import android.util.Slog;
@@ -42,8 +45,10 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.inputmethod.StartInputFlags;
+import com.android.server.LocalServices;
import com.android.server.textservices.TextServicesManagerInternal;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
@@ -1286,4 +1291,56 @@
return true;
}
+ /**
+ * Converts a user ID, which can be a pseudo user ID such as {@link UserHandle#USER_ALL} to a
+ * list of real user IDs.
+ *
+ * <p>Currently this method also converts profile user ID to profile parent user ID.</p>
+ *
+ * @param userIdToBeResolved A user ID. Two pseudo user ID {@link UserHandle#USER_CURRENT} and
+ * {@link UserHandle#USER_ALL} are also supported
+ * @param currentUserId A real user ID, which will be used when {@link UserHandle#USER_CURRENT}
+ * is specified in {@code userIdToBeResolved}.
+ * @param warningWriter A {@link PrintWriter} to output some debug messages. {@code null} if
+ * no debug message is required.
+ * @return An integer array that contain user IDs.
+ */
+ static int[] resolveUserId(@UserIdInt int userIdToBeResolved,
+ @UserIdInt int currentUserId, @Nullable PrintWriter warningWriter) {
+ final UserManagerInternal userManagerInternal =
+ LocalServices.getService(UserManagerInternal.class);
+
+ if (userIdToBeResolved == UserHandle.USER_ALL) {
+ final IntArray result = new IntArray();
+ for (int userId : userManagerInternal.getUserIds()) {
+ final int parentUserId = userManagerInternal.getProfileParentId(userId);
+ if (result.indexOf(parentUserId) < 0) {
+ result.add(parentUserId);
+ }
+ }
+ return result.toArray();
+ }
+
+ final int sourceUserId;
+ if (userIdToBeResolved == UserHandle.USER_CURRENT) {
+ sourceUserId = currentUserId;
+ } else if (userIdToBeResolved < 0) {
+ if (warningWriter != null) {
+ warningWriter.print("Pseudo user ID ");
+ warningWriter.print(userIdToBeResolved);
+ warningWriter.println(" is not supported.");
+ }
+ return new int[]{};
+ } else if (userManagerInternal.exists(userIdToBeResolved)) {
+ sourceUserId = userIdToBeResolved;
+ } else {
+ if (warningWriter != null) {
+ warningWriter.print("User #");
+ warningWriter.print(userIdToBeResolved);
+ warningWriter.println(" does not exit.");
+ }
+ return new int[]{};
+ }
+ return new int[]{userManagerInternal.getProfileParentId(sourceUserId)};
+ }
}
diff --git a/services/core/java/com/android/server/location/GnssConfiguration.java b/services/core/java/com/android/server/location/GnssConfiguration.java
new file mode 100644
index 0000000..29465ad
--- /dev/null
+++ b/services/core/java/com/android/server/location/GnssConfiguration.java
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * 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.android.server.location;
+
+import android.content.Context;
+import android.os.PersistableBundle;
+import android.os.SystemProperties;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+
+/**
+ * A utility class to hold GNSS configuration properties.
+ *
+ * The trigger to load/reload the configuration parameters should be managed by the class
+ * that owns an instance of this class.
+ *
+ * Instances of this class are not thread-safe and should either be used from a single thread
+ * or with external synchronization when used by multiple threads.
+ */
+class GnssConfiguration {
+ private static final String TAG = "GnssConfiguration";
+
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ //TODO(b/33112647): Create gps_debug.conf with commented career parameters.
+ private static final String DEBUG_PROPERTIES_FILE = "/etc/gps_debug.conf";
+
+ // config.xml properties
+ private static final String CONFIG_SUPL_HOST = "SUPL_HOST";
+ private static final String CONFIG_SUPL_PORT = "SUPL_PORT";
+ private static final String CONFIG_C2K_HOST = "C2K_HOST";
+ private static final String CONFIG_C2K_PORT = "C2K_PORT";
+ private static final String CONFIG_SUPL_VER = "SUPL_VER";
+ private static final String CONFIG_SUPL_MODE = "SUPL_MODE";
+ private static final String CONFIG_SUPL_ES = "SUPL_ES";
+ private static final String CONFIG_LPP_PROFILE = "LPP_PROFILE";
+ private static final String CONFIG_A_GLONASS_POS_PROTOCOL_SELECT =
+ "A_GLONASS_POS_PROTOCOL_SELECT";
+ private static final String CONFIG_USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL =
+ "USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL";
+ private static final String CONFIG_GPS_LOCK = "GPS_LOCK";
+ private static final String CONFIG_ES_EXTENSION_SEC = "ES_EXTENSION_SEC";
+
+ // Limit on NI emergency mode time extension after emergency sessions ends
+ private static final int MAX_EMERGENCY_MODE_EXTENSION_SECONDS = 300; // 5 minute maximum
+
+ // Persist property for LPP_PROFILE
+ static final String LPP_PROFILE = "persist.sys.gps.lpp";
+
+ // Represents an HAL interface version. Instances of this class are created in the JNI layer
+ // and returned through native methods.
+ private static class HalInterfaceVersion {
+ final int mMajor;
+ final int mMinor;
+
+ HalInterfaceVersion(int major, int minor) {
+ mMajor = major;
+ mMinor = minor;
+ }
+ }
+
+ /**
+ * Properties loaded from PROPERTIES_FILE.
+ */
+ private Properties mProperties;
+
+ private int mEsExtensionSec = 0;
+
+ private final Context mContext;
+
+ GnssConfiguration(Context context) {
+ mContext = context;
+ mProperties = new Properties();
+ }
+
+ /**
+ * Returns the full set of properties loaded.
+ */
+ Properties getProperties() {
+ return mProperties;
+ }
+
+ /**
+ * Returns the value of config parameter ES_EXTENSION_SEC. The value is range checked
+ * and constrained to min/max limits.
+ */
+ int getEsExtensionSec() {
+ return mEsExtensionSec;
+ }
+
+ /**
+ * Returns the value of config parameter SUPL_HOST or {@code null} if no value is
+ * provided.
+ */
+ String getSuplHost() {
+ return mProperties.getProperty(CONFIG_SUPL_HOST);
+ }
+
+ /**
+ * Returns the value of config parameter SUPL_PORT or {@code defaultPort} if no value is
+ * provided or if there is an error parsing the configured value.
+ */
+ int getSuplPort(int defaultPort) {
+ return getIntConfig(CONFIG_SUPL_PORT, defaultPort);
+ }
+
+ /**
+ * Returns the value of config parameter C2K_HOST or {@code null} if no value is
+ * provided.
+ */
+ String getC2KHost() {
+ return mProperties.getProperty(CONFIG_C2K_HOST);
+ }
+
+ /**
+ * Returns the value of config parameter C2K_PORT or {@code defaultPort} if no value is
+ * provided or if there is an error parsing the configured value.
+ */
+ int getC2KPort(int defaultPort) {
+ return getIntConfig(CONFIG_C2K_PORT, defaultPort);
+ }
+
+ /**
+ * Returns the value of config parameter SUPL_MODE or {@code defaultMode} if no value is
+ * provided or if there is an error parsing the configured value.
+ */
+ int getSuplMode(int defaultMode) {
+ return getIntConfig(CONFIG_SUPL_MODE, defaultMode);
+ }
+
+ /**
+ * Returns the value of config parameter SUPL_ES or {@code defaultSuplEs} if no value is
+ * provided or if there is an error parsing the configured value.
+ */
+ int getSuplEs(int defaulSuplEs) {
+ return getIntConfig(CONFIG_SUPL_ES, defaulSuplEs);
+ }
+
+ /**
+ * Returns the value of config parameter LPP_PROFILE or {@code null} if no value is
+ * provided.
+ */
+ String getLppProfile() {
+ return mProperties.getProperty(CONFIG_LPP_PROFILE);
+ }
+
+ /**
+ * Updates the GNSS HAL satellite blacklist.
+ */
+ void setSatelliteBlacklist(int[] constellations, int[] svids) {
+ native_set_satellite_blacklist(constellations, svids);
+ }
+
+ interface SetCarrierProperty {
+ boolean set(int value);
+ }
+
+ /**
+ * Loads the GNSS properties from carrier config file followed by the properties from
+ * gps debug config file.
+ */
+ void reloadGpsProperties() {
+ if (DEBUG) Log.d(TAG, "Reset GPS properties, previous size = " + mProperties.size());
+ loadPropertiesFromCarrierConfig();
+
+ String lpp_prof = SystemProperties.get(LPP_PROFILE);
+ if (!TextUtils.isEmpty(lpp_prof)) {
+ // override default value of this if lpp_prof is not empty
+ mProperties.setProperty(CONFIG_LPP_PROFILE, lpp_prof);
+ }
+ /*
+ * Overlay carrier properties from a debug configuration file.
+ */
+ loadPropertiesFromGpsDebugConfig(mProperties);
+
+ mEsExtensionSec = getRangeCheckedConfigEsExtensionSec();
+
+ final HalInterfaceVersion gnssConfigurationIfaceVersion =
+ native_get_gnss_configuration_version();
+ if (gnssConfigurationIfaceVersion != null) {
+ // Set to a range checked value.
+ if (isConfigEsExtensionSecSupported(gnssConfigurationIfaceVersion)
+ && !native_set_es_extension_sec(mEsExtensionSec)) {
+ Log.e(TAG, "Unable to set " + CONFIG_ES_EXTENSION_SEC + ": " + mEsExtensionSec);
+ }
+
+ Map<String, SetCarrierProperty> map = new HashMap<String, SetCarrierProperty>() {
+ {
+ put(CONFIG_SUPL_VER, GnssConfiguration::native_set_supl_version);
+ put(CONFIG_SUPL_MODE, GnssConfiguration::native_set_supl_mode);
+
+ if (isConfigSuplEsSupported(gnssConfigurationIfaceVersion)) {
+ put(CONFIG_SUPL_ES, GnssConfiguration::native_set_supl_es);
+ }
+
+ put(CONFIG_LPP_PROFILE, GnssConfiguration::native_set_lpp_profile);
+ put(CONFIG_A_GLONASS_POS_PROTOCOL_SELECT,
+ GnssConfiguration::native_set_gnss_pos_protocol_select);
+ put(CONFIG_USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL,
+ GnssConfiguration::native_set_emergency_supl_pdn);
+
+ if (isConfigGpsLockSupported(gnssConfigurationIfaceVersion)) {
+ put(CONFIG_GPS_LOCK, GnssConfiguration::native_set_gps_lock);
+ }
+ }
+ };
+
+ for (Entry<String, SetCarrierProperty> entry : map.entrySet()) {
+ String propertyName = entry.getKey();
+ String propertyValueString = mProperties.getProperty(propertyName);
+ if (propertyValueString != null) {
+ try {
+ int propertyValueInt = Integer.decode(propertyValueString);
+ boolean result = entry.getValue().set(propertyValueInt);
+ if (!result) {
+ Log.e(TAG, "Unable to set " + propertyName);
+ }
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "Unable to parse propertyName: " + propertyValueString);
+ }
+ }
+ }
+ } else if (DEBUG) {
+ Log.d(TAG, "Skipped configuration update because GNSS configuration in GPS HAL is not"
+ + " supported");
+ }
+ }
+
+ /**
+ * Loads GNSS properties from carrier config file.
+ */
+ void loadPropertiesFromCarrierConfig() {
+ CarrierConfigManager configManager = (CarrierConfigManager)
+ mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (configManager == null) {
+ return;
+ }
+ PersistableBundle configs = configManager.getConfigForSubId(
+ SubscriptionManager.getDefaultDataSubscriptionId());
+ if (configs == null) {
+ if (DEBUG) Log.d(TAG, "SIM not ready, use default carrier config.");
+ configs = CarrierConfigManager.getDefaultConfig();
+ }
+ for (String configKey : configs.keySet()) {
+ if (configKey.startsWith(CarrierConfigManager.Gps.KEY_PREFIX)) {
+ String key = configKey
+ .substring(CarrierConfigManager.Gps.KEY_PREFIX.length())
+ .toUpperCase();
+ Object value = configs.get(configKey);
+ if (value instanceof String) {
+ // All GPS properties are of String type; convert so.
+ if (DEBUG) Log.d(TAG, "Gps config: " + key + " = " + value);
+ mProperties.setProperty(key, (String) value);
+ }
+ }
+ }
+ }
+
+ private void loadPropertiesFromGpsDebugConfig(Properties properties) {
+ try {
+ File file = new File(DEBUG_PROPERTIES_FILE);
+ FileInputStream stream = null;
+ try {
+ stream = new FileInputStream(file);
+ properties.load(stream);
+ } finally {
+ IoUtils.closeQuietly(stream);
+ }
+ } catch (IOException e) {
+ if (DEBUG) Log.d(TAG, "Could not open GPS configuration file " + DEBUG_PROPERTIES_FILE);
+ }
+ }
+
+ private int getRangeCheckedConfigEsExtensionSec() {
+ int emergencyExtensionSeconds = getIntConfig(CONFIG_ES_EXTENSION_SEC, 0);
+ if (emergencyExtensionSeconds > MAX_EMERGENCY_MODE_EXTENSION_SECONDS) {
+ Log.w(TAG, CONFIG_ES_EXTENSION_SEC + ": " + emergencyExtensionSeconds
+ + " too high, reset to " + MAX_EMERGENCY_MODE_EXTENSION_SECONDS);
+ emergencyExtensionSeconds = MAX_EMERGENCY_MODE_EXTENSION_SECONDS;
+ } else if (emergencyExtensionSeconds < 0) {
+ Log.w(TAG, CONFIG_ES_EXTENSION_SEC + ": " + emergencyExtensionSeconds
+ + " is negative, reset to zero.");
+ emergencyExtensionSeconds = 0;
+ }
+ return emergencyExtensionSeconds;
+ }
+
+ private int getIntConfig(String configParameter, int defaultValue) {
+ String valueString = mProperties.getProperty(configParameter);
+ if (TextUtils.isEmpty(valueString)) {
+ return defaultValue;
+ }
+ try {
+ return Integer.parseInt(valueString);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "Unable to parse config parameter " + configParameter + " value: "
+ + valueString + ". Using default value: " + defaultValue);
+ return defaultValue;
+ }
+ }
+
+ private static boolean isConfigEsExtensionSecSupported(
+ HalInterfaceVersion gnssConfiguartionIfaceVersion) {
+ // ES_EXTENSION_SEC is introduced in @2.0::IGnssConfiguration.hal
+ return gnssConfiguartionIfaceVersion.mMajor >= 2;
+ }
+
+ private static boolean isConfigSuplEsSupported(
+ HalInterfaceVersion gnssConfiguartionIfaceVersion) {
+ // SUPL_ES is deprecated in @2.0::IGnssConfiguration.hal
+ return gnssConfiguartionIfaceVersion.mMajor < 2;
+ }
+
+ private static boolean isConfigGpsLockSupported(
+ HalInterfaceVersion gnssConfiguartionIfaceVersion) {
+ // GPS_LOCK is deprecated in @2.0::IGnssConfiguration.hal
+ return gnssConfiguartionIfaceVersion.mMajor < 2;
+ }
+
+ private static native HalInterfaceVersion native_get_gnss_configuration_version();
+
+ private static native boolean native_set_supl_version(int version);
+
+ private static native boolean native_set_supl_mode(int mode);
+
+ private static native boolean native_set_supl_es(int es);
+
+ private static native boolean native_set_lpp_profile(int lppProfile);
+
+ private static native boolean native_set_gnss_pos_protocol_select(int gnssPosProtocolSelect);
+
+ private static native boolean native_set_gps_lock(int gpsLock);
+
+ private static native boolean native_set_emergency_supl_pdn(int emergencySuplPdn);
+
+ private static native boolean native_set_satellite_blacklist(int[] constellations, int[] svIds);
+
+ private static native boolean native_set_es_extension_sec(int emergencyExtensionSeconds);
+}
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 05d77ab..3c81a45 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -74,12 +74,7 @@
import com.android.server.location.GnssSatelliteBlacklistHelper.GnssSatelliteBlacklistCallback;
import com.android.server.location.NtpTimeHelper.InjectNtpTimeCallback;
-import libcore.io.IoUtils;
-
-import java.io.File;
import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@@ -87,11 +82,7 @@
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Properties;
/**
* A GNSS implementation of LocationProvider used by LocationManager.
@@ -147,7 +138,6 @@
private static final int LOCATION_HAS_SPEED_ACCURACY = 64;
private static final int LOCATION_HAS_BEARING_ACCURACY = 128;
-
// IMPORTANT - the GPS_DELETE_* symbols here must match GnssAidingData enum in IGnss.hal
private static final int GPS_DELETE_EPHEMERIS = 0x0001;
private static final int GPS_DELETE_ALMANAC = 0x0002;
@@ -200,9 +190,6 @@
private static final int AGPS_RIL_REQUEST_SETID_IMSI = 1;
private static final int AGPS_RIL_REQUEST_SETID_MSISDN = 2;
- //TODO(b/33112647): Create gps_debug.conf with commented career parameters.
- private static final String DEBUG_PROPERTIES_FILE = "/etc/gps_debug.conf";
-
// ref. location info
private static final int AGPS_REF_LOCATION_TYPE_GSM_CELLID = 1;
private static final int AGPS_REF_LOCATION_TYPE_UMTS_CELLID = 2;
@@ -375,7 +362,7 @@
* Properties loaded from PROPERTIES_FILE.
* It must be accessed only inside {@link #mHandler}.
*/
- private Properties mProperties;
+ private GnssConfiguration mGnssConfiguration;
private String mSuplServerHost;
private int mSuplServerPort = TCP_MIN_PORT;
@@ -411,10 +398,6 @@
private final static String ALARM_WAKEUP = "com.android.internal.location.ALARM_WAKEUP";
private final static String ALARM_TIMEOUT = "com.android.internal.location.ALARM_TIMEOUT";
- // Persist property for LPP_PROFILE
- private final static String LPP_PROFILE = "persist.sys.gps.lpp";
-
-
private final PowerManager mPowerManager;
private final AlarmManager mAlarmManager;
private final PendingIntent mWakeupIntent;
@@ -502,7 +485,7 @@
*/
@Override
public void onUpdateSatelliteBlacklist(int[] constellations, int[] svids) {
- mHandler.post(() -> native_set_satellite_blacklist(constellations, svids));
+ mHandler.post(() -> mGnssConfiguration.setSatelliteBlacklist(constellations, svids));
}
private void subscriptionOrCarrierConfigChanged(Context context) {
@@ -525,17 +508,17 @@
}
if (isKeepLppProfile) {
// load current properties for the carrier
- loadPropertiesFromCarrierConfig(context, mProperties);
- String lpp_profile = mProperties.getProperty("LPP_PROFILE");
+ mGnssConfiguration.loadPropertiesFromCarrierConfig();
+ String lpp_profile = mGnssConfiguration.getLppProfile();
// set the persist property LPP_PROFILE for the value
if (lpp_profile != null) {
- SystemProperties.set(LPP_PROFILE, lpp_profile);
+ SystemProperties.set(GnssConfiguration.LPP_PROFILE, lpp_profile);
}
} else {
// reset the persist property
- SystemProperties.set(LPP_PROFILE, "");
+ SystemProperties.set(GnssConfiguration.LPP_PROFILE, "");
}
- reloadGpsProperties(context, mProperties);
+ reloadGpsProperties();
mNIHandler.setSuplEsEnabled(mSuplEsEnabled);
}
} else {
@@ -564,133 +547,14 @@
return native_is_supported();
}
- interface SetCarrierProperty {
- boolean set(int value);
- }
-
- private void reloadGpsProperties(Context context, Properties properties) {
- if (DEBUG) Log.d(TAG, "Reset GPS properties, previous size = " + properties.size());
- loadPropertiesFromCarrierConfig(context, properties);
-
- String lpp_prof = SystemProperties.get(LPP_PROFILE);
- if (!TextUtils.isEmpty(lpp_prof)) {
- // override default value of this if lpp_prof is not empty
- properties.setProperty("LPP_PROFILE", lpp_prof);
- }
- /*
- * Overlay carrier properties from a debug configuration file.
- */
- loadPropertiesFromGpsDebugConfig(properties);
+ private void reloadGpsProperties() {
+ mGnssConfiguration.reloadGpsProperties();
+ setSuplHostPort();
// TODO: we should get rid of C2K specific setting.
- setSuplHostPort(properties.getProperty("SUPL_HOST"),
- properties.getProperty("SUPL_PORT"));
- mC2KServerHost = properties.getProperty("C2K_HOST");
- String portString = properties.getProperty("C2K_PORT");
- if (mC2KServerHost != null && portString != null) {
- try {
- mC2KServerPort = Integer.parseInt(portString);
- } catch (NumberFormatException e) {
- Log.e(TAG, "unable to parse C2K_PORT: " + portString);
- }
- }
- if (native_is_gnss_configuration_supported()) {
- Map<String, SetCarrierProperty> map = new HashMap<String, SetCarrierProperty>() {
- {
- put("SUPL_VER", GnssLocationProvider::native_set_supl_version);
- put("SUPL_MODE", GnssLocationProvider::native_set_supl_mode);
- put("SUPL_ES", GnssLocationProvider::native_set_supl_es);
- put("LPP_PROFILE", GnssLocationProvider::native_set_lpp_profile);
- put("A_GLONASS_POS_PROTOCOL_SELECT",
- GnssLocationProvider::native_set_gnss_pos_protocol_select);
- put("USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL",
- GnssLocationProvider::native_set_emergency_supl_pdn);
- put("GPS_LOCK", GnssLocationProvider::native_set_gps_lock);
- }
- };
-
- for (Entry<String, SetCarrierProperty> entry : map.entrySet()) {
- String propertyName = entry.getKey();
- String propertyValueString = properties.getProperty(propertyName);
- if (propertyValueString != null) {
- try {
- int propertyValueInt = Integer.decode(propertyValueString);
- boolean result = entry.getValue().set(propertyValueInt);
- if (!result) {
- Log.e(TAG, "Unable to set " + propertyName);
- }
- } catch (NumberFormatException e) {
- Log.e(TAG, "unable to parse propertyName: " + propertyValueString);
- }
- }
- }
- } else if (DEBUG) {
- Log.d(TAG, "Skipped configuration update because GNSS configuration in GPS HAL is not"
- + " supported");
- }
-
- // SUPL_ES configuration.
- String suplESProperty = mProperties.getProperty("SUPL_ES");
- if (suplESProperty != null) {
- try {
- mSuplEsEnabled = (Integer.parseInt(suplESProperty) == 1);
- } catch (NumberFormatException e) {
- Log.e(TAG, "unable to parse SUPL_ES: " + suplESProperty);
- }
- }
-
- String emergencyExtensionSecondsString
- = properties.getProperty("ES_EXTENSION_SEC", "0");
- try {
- int emergencyExtensionSeconds =
- Integer.parseInt(emergencyExtensionSecondsString);
- mNIHandler.setEmergencyExtensionSeconds(emergencyExtensionSeconds);
- } catch (NumberFormatException e) {
- Log.e(TAG, "unable to parse ES_EXTENSION_SEC: "
- + emergencyExtensionSecondsString);
- }
- }
-
- private void loadPropertiesFromCarrierConfig(Context context, Properties properties) {
- CarrierConfigManager configManager = (CarrierConfigManager)
- mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- if (configManager == null) {
- return;
- }
- PersistableBundle configs = configManager.getConfigForSubId(
- SubscriptionManager.getDefaultDataSubscriptionId());
- if (configs == null) {
- if (DEBUG) Log.d(TAG, "SIM not ready, use default carrier config.");
- configs = CarrierConfigManager.getDefaultConfig();
- }
- for (String configKey : configs.keySet()) {
- if (configKey.startsWith(CarrierConfigManager.Gps.KEY_PREFIX)) {
- String key = configKey
- .substring(CarrierConfigManager.Gps.KEY_PREFIX.length())
- .toUpperCase();
- Object value = configs.get(configKey);
- if (value instanceof String) {
- // All GPS properties are of String type; convert so.
- if (DEBUG) Log.d(TAG, "Gps config: " + key + " = " + value);
- properties.setProperty(key, (String) value);
- }
- }
- }
- }
-
- private void loadPropertiesFromGpsDebugConfig(Properties properties) {
- try {
- File file = new File(DEBUG_PROPERTIES_FILE);
- FileInputStream stream = null;
- try {
- stream = new FileInputStream(file);
- properties.load(stream);
- } finally {
- IoUtils.closeQuietly(stream);
- }
-
- } catch (IOException e) {
- if (DEBUG) Log.d(TAG, "Could not open GPS configuration file " + DEBUG_PROPERTIES_FILE);
- }
+ mC2KServerHost = mGnssConfiguration.getC2KHost();
+ mC2KServerPort = mGnssConfiguration.getC2KPort(TCP_MIN_PORT);
+ mNIHandler.setEmergencyExtensionSeconds(mGnssConfiguration.getEsExtensionSec());
+ mSuplEsEnabled = mGnssConfiguration.getSuplEs(0) == 1;
}
public GnssLocationProvider(Context context, LocationProviderManager locationProviderManager,
@@ -733,7 +597,7 @@
// relative long time, so the ctor() is kept to create objects needed by this instance,
// while IO initialization and registration is delegated to our internal handler
// this approach is just fine because events are posted to our handler anyway
- mProperties = new Properties();
+ mGnssConfiguration = new GnssConfiguration(mContext);
sendMessage(INITIALIZE_HANDLER, 0, null);
// Create a GPS net-initiated handler.
@@ -918,7 +782,8 @@
mDownloadXtraWakeLock.acquire(DOWNLOAD_XTRA_DATA_TIMEOUT_MS);
Log.i(TAG, "WakeLock acquired by handleDownloadXtraData()");
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
- GpsXtraDownloader xtraDownloader = new GpsXtraDownloader(mProperties);
+ GpsXtraDownloader xtraDownloader = new GpsXtraDownloader(
+ mGnssConfiguration.getProperties());
byte[] data = xtraDownloader.downloadXtraData();
if (data != null) {
if (DEBUG) Log.d(TAG, "calling native_inject_xtra_data");
@@ -963,17 +828,9 @@
}
}
- private void setSuplHostPort(String hostString, String portString) {
- if (hostString != null) {
- mSuplServerHost = hostString;
- }
- if (portString != null) {
- try {
- mSuplServerPort = Integer.parseInt(portString);
- } catch (NumberFormatException e) {
- Log.e(TAG, "unable to parse SUPL_PORT: " + portString);
- }
- }
+ private void setSuplHostPort() {
+ mSuplServerHost = mGnssConfiguration.getSuplHost();
+ mSuplServerPort = mGnssConfiguration.getSuplPort(TCP_MIN_PORT);
if (mSuplServerHost != null
&& mSuplServerPort > TCP_MIN_PORT
&& mSuplServerPort <= TCP_MAX_PORT) {
@@ -986,23 +843,17 @@
* Checks what SUPL mode to use, according to the AGPS mode as well as the
* allowed mode from properties.
*
- * @param properties GPS properties
* @param agpsEnabled whether AGPS is enabled by settings value
* @param singleShot whether "singleshot" is needed
* @return SUPL mode (MSA vs MSB vs STANDALONE)
*/
- private int getSuplMode(Properties properties, boolean agpsEnabled, boolean singleShot) {
+ private int getSuplMode(boolean agpsEnabled, boolean singleShot) {
if (agpsEnabled) {
- String modeString = properties.getProperty("SUPL_MODE");
- int suplMode = 0;
- if (!TextUtils.isEmpty(modeString)) {
- try {
- suplMode = Integer.parseInt(modeString);
- } catch (NumberFormatException e) {
- Log.e(TAG, "unable to parse SUPL_MODE: " + modeString);
- return GPS_POSITION_MODE_STANDALONE;
- }
+ int suplMode = mGnssConfiguration.getSuplMode(0);
+ if (suplMode == 0) {
+ return GPS_POSITION_MODE_STANDALONE;
}
+
// MS-Based is the preferred mode for Assisted-GPS position computation, so we favor
// such mode when it is available
if (hasCapability(GPS_CAPABILITY_MSB) && (suplMode & AGPS_SUPL_MODE_MSB) != 0) {
@@ -1307,7 +1158,7 @@
boolean agpsEnabled =
(Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.ASSISTED_GPS_ENABLED, 1) != 0);
- mPositionMode = getSuplMode(mProperties, agpsEnabled, singleShot);
+ mPositionMode = getSuplMode(agpsEnabled, singleShot);
if (DEBUG) {
String mode;
@@ -1670,7 +1521,7 @@
// re-calls native_init() and other setup.
handleEnable();
// resend configuration into the restarted HAL service.
- reloadGpsProperties(mContext, mProperties);
+ reloadGpsProperties();
}
});
}
@@ -2072,7 +1923,7 @@
// load default GPS configuration
// (this configuration might change in the future based on SIM changes)
- reloadGpsProperties(mContext, mProperties);
+ reloadGpsProperties();
// TODO: When this object "finishes" we should unregister by invoking
// SubscriptionManager.getInstance(mContext).unregister
@@ -2227,8 +2078,6 @@
private static native boolean native_is_supported();
- private static native boolean native_is_gnss_configuration_supported();
-
private static native void native_init_once();
private native boolean native_init();
@@ -2284,21 +2133,4 @@
int lac, int cid);
private native void native_agps_set_id(int type, String setid);
-
- // GNSS Configuration
- private static native boolean native_set_supl_version(int version);
-
- private static native boolean native_set_supl_mode(int mode);
-
- private static native boolean native_set_supl_es(int es);
-
- private static native boolean native_set_lpp_profile(int lppProfile);
-
- private static native boolean native_set_gnss_pos_protocol_select(int gnssPosProtocolSelect);
-
- private static native boolean native_set_gps_lock(int gpsLock);
-
- private static native boolean native_set_emergency_supl_pdn(int emergencySuplPdn);
-
- private static native boolean native_set_satellite_blacklist(int[] constellations, int[] svIds);
-}
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index b70c64e..8ecceb9 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -116,7 +116,7 @@
private boolean mIsActive = false;
private boolean mDestroyed = false;
- private long mDuration;
+ private long mDuration = -1;
private String mMetadataDescription;
public MediaSessionRecord(int ownerPid, int ownerUid, int userId, String ownerPackageName,
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 052d579..7f2e047 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -40,6 +40,8 @@
import android.media.AudioSystem;
import android.media.IAudioService;
import android.media.IRemoteVolumeController;
+import android.media.MediaController2;
+import android.media.Session2CommandGroup;
import android.media.Session2Token;
import android.media.session.IActiveSessionsListener;
import android.media.session.ICallback;
@@ -54,6 +56,7 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.Message;
import android.os.PowerManager;
@@ -73,6 +76,7 @@
import android.view.KeyEvent;
import android.view.ViewConfiguration;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.DumpUtils;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -97,17 +101,23 @@
private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000;
private final SessionManagerImpl mSessionManagerImpl;
-
- // Keeps the full user id for each user.
- private final SparseIntArray mFullUserIds = new SparseIntArray();
- private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<FullUserRecord>();
- private final ArrayList<SessionsListenerRecord> mSessionsListeners
- = new ArrayList<SessionsListenerRecord>();
- private final Object mLock = new Object();
private final MessageHandler mHandler = new MessageHandler();
private final PowerManager.WakeLock mMediaEventWakeLock;
private final int mLongPressTimeout;
private final INotificationManager mNotificationManager;
+ private final Object mLock = new Object();
+ // Keeps the full user id for each user.
+ @GuardedBy("mLock")
+ private final SparseIntArray mFullUserIds = new SparseIntArray();
+ @GuardedBy("mLock")
+ private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<FullUserRecord>();
+ @GuardedBy("mLock")
+ private final ArrayList<SessionsListenerRecord> mSessionsListeners
+ = new ArrayList<SessionsListenerRecord>();
+ // TODO: Keep session2 info in MediaSessionStack for prioritizing both session1 and session2 in
+ // one place.
+ @GuardedBy("mLock")
+ private final List<Session2Token> mSession2Tokens = new ArrayList<>();
private KeyguardManager mKeyguardManager;
private IAudioService mAudioService;
@@ -722,6 +732,10 @@
pw.println(indent + "Restored MediaButtonReceiverComponentType: "
+ mRestoredMediaButtonReceiverComponentType);
mPriorityStack.dump(pw, indent);
+ pw.println(indent + "Session2Tokens - " + mSession2Tokens.size());
+ for (Session2Token session2Token : mSession2Tokens) {
+ pw.println(indent + " " + session2Token);
+ }
}
@Override
@@ -904,7 +918,17 @@
if (DEBUG) {
Log.d(TAG, "Session2 is created " + sessionToken);
}
- // TODO: Keep the session.
+ if (uid != sessionToken.getUid()) {
+ throw new SecurityException("Unexpected Session2Token's UID, expected=" + uid
+ + " but actually=" + sessionToken.getUid());
+ }
+ Controller2Callback callback = new Controller2Callback(sessionToken);
+ // Note: It's safe not to keep controller here because it wouldn't be GC'ed until
+ // it's closed.
+ // TODO: Keep controller as well for better readability
+ // because the GC behavior isn't straightforward.
+ MediaController2 controller = new MediaController2(getContext(), sessionToken,
+ new HandlerExecutor(mHandler), callback);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1930,4 +1954,26 @@
obtainMessage(MSG_SESSIONS_CHANGED, userIdInteger).sendToTarget();
}
}
+
+ private class Controller2Callback extends MediaController2.ControllerCallback {
+ private final Session2Token mToken;
+
+ Controller2Callback(Session2Token token) {
+ mToken = token;
+ }
+
+ @Override
+ public void onConnected(MediaController2 controller, Session2CommandGroup allowedCommands) {
+ synchronized (mLock) {
+ mSession2Tokens.add(mToken);
+ }
+ }
+
+ @Override
+ public void onDisconnected(MediaController2 controller) {
+ synchronized (mLock) {
+ mSession2Tokens.remove(mToken);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 522ab0b..3301962 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -15618,13 +15618,25 @@
if (!hasDynamicLibraries) {
return null;
}
+ final boolean isUpdatedSystemApp = pkg.isUpdatedSystemApp();
+ // We may not yet have disabled the updated package yet, so be sure to grab the
+ // current setting if that's the case.
+ final PackageSetting updatedSystemPs = isUpdatedSystemApp
+ ? scanResult.request.disabledPkgSetting == null
+ ? scanResult.request.oldPkgSetting
+ : scanResult.request.disabledPkgSetting
+ : null;
+ if (isUpdatedSystemApp && (updatedSystemPs.pkg == null
+ || updatedSystemPs.pkg.libraryNames == null)) {
+ Slog.w(TAG, "Package " + pkg.packageName + " declares libraries that are not "
+ + "declared on the system image; skipping");
+ return null;
+ }
final ArrayList<SharedLibraryInfo> infos =
new ArrayList<>(scanResult.dynamicSharedLibraryInfos.size());
- final boolean updatedSystemApp = pkg.isUpdatedSystemApp();
for (SharedLibraryInfo info : scanResult.dynamicSharedLibraryInfos) {
- String name = info.getName();
- boolean allowed = false;
- if (updatedSystemApp) {
+ final String name = info.getName();
+ if (isUpdatedSystemApp) {
// New library entries can only be added through the
// system image. This is important to get rid of a lot
// of nasty edge cases: for example if we allowed a non-
@@ -15634,36 +15646,20 @@
// have allowed apps on the device which aren't compatible
// with it. Better to just have the restriction here, be
// conservative, and create many fewer cases that can negatively
- // impact the user experience. We may not yet have disabled the
- // updated package yet, so be sure to grab the current setting if
- // that's the case.
- final PackageSetting sysPs = scanResult.request.disabledPkgSetting == null
- ? scanResult.request.oldPkgSetting
- : scanResult.request.disabledPkgSetting;
- if (sysPs.pkg != null && sysPs.pkg.libraryNames != null) {
- for (int j = 0; j < sysPs.pkg.libraryNames.size(); j++) {
- if (name.equals(sysPs.pkg.libraryNames.get(j))) {
- allowed = true;
- break;
- }
- }
- }
- } else {
- allowed = true;
- }
- if (allowed) {
- if (sharedLibExists(
- name, SharedLibraryInfo.VERSION_UNDEFINED, existingSharedLibraries)) {
- Slog.w(TAG, "Package " + pkg.packageName + " library "
- + name + " already exists; skipping");
+ // impact the user experience.
+ if (!updatedSystemPs.pkg.libraryNames.contains(name)) {
+ Slog.w(TAG, "Package " + pkg.packageName + " declares library " + name
+ + " that is not declared on system image; skipping");
continue;
}
- infos.add(info);
- } else {
- Slog.w(TAG, "Package " + pkg.packageName + " declares lib "
- + name + " that is not declared on system image; skipping");
+ }
+ if (sharedLibExists(
+ name, SharedLibraryInfo.VERSION_UNDEFINED, existingSharedLibraries)) {
+ Slog.w(TAG, "Package " + pkg.packageName + " declares library " + name
+ + " that already exists; skipping");
continue;
}
+ infos.add(info);
}
return infos;
}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 2157ef6..801c1e7 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -2356,7 +2356,7 @@
public void onAnimationLeashDestroyed(Transaction t) {
super.onAnimationLeashDestroyed(t);
if (mAnimationBoundsLayer != null) {
- t.destroy(mAnimationBoundsLayer);
+ t.reparent(mAnimationBoundsLayer, null);
mAnimationBoundsLayer = null;
}
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index aea071f..c39060e 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -129,7 +129,7 @@
final DimAnimatable dimAnimatable = new DimAnimatable(dimLayer);
mSurfaceAnimator = new SurfaceAnimator(dimAnimatable, () -> {
if (!mDimming) {
- dimAnimatable.getPendingTransaction().destroy(mDimLayer);
+ dimAnimatable.getPendingTransaction().reparent(mDimLayer, null);
}
}, mHost.mWmService);
}
@@ -300,7 +300,7 @@
if (!mDimState.mDimming) {
if (!mDimState.mAnimateExit) {
- t.destroy(mDimState.mDimLayer);
+ t.reparent(mDimState.mDimLayer, null);
} else {
startDimExit(mLastRequestedDimContainer, mDimState.mSurfaceAnimator, t);
}
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 607ee76..786a306 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -127,6 +127,7 @@
* {@code true} when {@link #closeLocked()} is called.
*/
private boolean mIsClosing;
+ IBinder mTransferTouchFromToken;
DragState(WindowManagerService service, DragDropController controller, IBinder token,
SurfaceControl surface, int flags, IBinder localWin) {
@@ -177,6 +178,8 @@
mTmpClipRect.set(0, 0, mDisplaySize.x, mDisplaySize.y);
t.setWindowCrop(mInputSurface, mTmpClipRect);
+ t.transferTouchFocus(mTransferTouchFromToken, h.token);
+ mTransferTouchFromToken = null;
}
/**
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 639ed02..f9c9d33 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -1,5 +1,6 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -9,7 +10,6 @@
import android.os.Debug;
import android.os.IBinder;
import android.util.Slog;
-import android.view.InputApplicationHandle;
import android.view.KeyEvent;
import android.view.WindowManager;
@@ -204,6 +204,37 @@
+ WindowManagerService.TYPE_LAYER_OFFSET;
}
+ /** Callback to get pointer display id. */
+ @Override
+ public int getPointerDisplayId() {
+ synchronized (mService.mGlobalLock) {
+ // If desktop mode is not enabled, show on the default display.
+ if (!mService.mForceDesktopModeOnExternalDisplays) {
+ return DEFAULT_DISPLAY;
+ }
+
+ // Look for the topmost freeform display.
+ int firstExternalDisplayId = DEFAULT_DISPLAY;
+ for (int i = mService.mRoot.mChildren.size() - 1; i >= 0; --i) {
+ final DisplayContent displayContent = mService.mRoot.mChildren.get(i);
+ // Heuristic solution here. Currently when "Freeform windows" developer option is
+ // enabled we automatically put secondary displays in freeform mode and emulating
+ // "desktop mode". It also makes sense to show the pointer on the same display.
+ if (displayContent.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
+ return displayContent.getDisplayId();
+ }
+
+ if (firstExternalDisplayId == DEFAULT_DISPLAY
+ && displayContent.getDisplayId() != DEFAULT_DISPLAY) {
+ firstExternalDisplayId = displayContent.getDisplayId();
+ }
+ }
+
+ // Look for the topmost non-default display
+ return firstExternalDisplayId;
+ }
+ }
+
/** Waits until the built-in input devices have been configured. */
public boolean waitForInputDevicesReady(long timeoutMillis) {
synchronized (mInputDevicesReadyMonitor) {
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index cc57b932..bc01f7c 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -202,6 +202,9 @@
}
private void notifyPendingInsetsControlChanged() {
+ if (mPendingControlChanged.isEmpty()) {
+ return;
+ }
mDisplayContent.mWmService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
for (int i = mPendingControlChanged.size() - 1; i >= 0; i--) {
final WindowState controllingWin = mPendingControlChanged.valueAt(i);
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 11068ce..9d9b48a 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -280,7 +280,7 @@
}
mService.mAnimationTransferMap.remove(mAnimation);
if (mLeash != null && destroyLeash) {
- t.destroy(mLeash);
+ t.reparent(mLeash, null);
scheduleAnim = true;
}
mLeash = null;
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index e15bf5b..9163165 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -24,6 +24,7 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Slog;
@@ -50,6 +51,7 @@
private @Nullable TaskPositioner mTaskPositioner;
private final Rect mTmpClipRect = new Rect();
+ private IBinder mTransferTouchFromToken;
boolean isPositioningLocked() {
return mTaskPositioner != null;
@@ -102,6 +104,8 @@
mTmpClipRect.set(0, 0, p.x, p.y);
t.setWindowCrop(mInputSurface, mTmpClipRect);
+ t.transferTouchFocus(mTransferTouchFromToken, h.token);
+ mTransferTouchFromToken = null;
}
boolean startMovingTask(IWindow window, float startX, float startY) {
@@ -170,7 +174,6 @@
mPositioningDisplay = displayContent;
mTaskPositioner = TaskPositioner.create(mService);
- mTaskPositioner.register(displayContent);
// We need to grab the touch focus so that the touch events during the
// resizing/scrolling are not sent to the app. 'win' is the main window
@@ -181,12 +184,8 @@
&& displayContent.mCurrentFocus.mAppToken == win.mAppToken) {
transferFocusFromWin = displayContent.mCurrentFocus;
}
- if (!mInputManager.transferTouchFocus(
- transferFocusFromWin.mInputChannel, mTaskPositioner.mServerChannel)) {
- Slog.e(TAG_WM, "startPositioningLocked: Unable to transfer touch focus");
- cleanUpTaskPositioner();
- return false;
- }
+ mTransferTouchFromToken = transferFocusFromWin.mInputChannel.getToken();
+ mTaskPositioner.register(displayContent);
mTaskPositioner.startDrag(win, resize, preserveOrientation, startX, startY);
return true;
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index 1d56f04..4a553cf 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -974,10 +974,11 @@
*/
boolean isSameIntentFilter(ActivityRecord r) {
final Intent intent = new Intent(r.intent);
- // Correct the activity intent for aliasing. The task record intent will always be based on
- // the real activity that will be launched not the alias, so we need to use an intent with
- // the component name pointing to the real activity not the alias in the activity record.
- intent.setComponent(r.mActivityComponent);
+ // Make sure the component are the same if the input activity has the same real activity
+ // as the one in the task because either one of them could be the alias activity.
+ if (Objects.equals(realActivity, r.mActivityComponent) && this.intent != null) {
+ intent.setComponent(this.intent.getComponent());
+ }
return intent.filterEquals(this.intent);
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 32c5a3b..25e61f8 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -326,7 +326,7 @@
}
if (mSurfaceControl != null) {
- mPendingTransaction.destroy(mSurfaceControl);
+ mPendingTransaction.reparent(mSurfaceControl, null);
// Merge to parent transaction to ensure the transactions on this WindowContainer are
// applied in native even if WindowContainer is removed.
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 646fdd9..1691dc0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -158,8 +158,9 @@
default boolean registerInputChannel(
DragState state, Display display, InputManagerService service,
InputChannel source) {
+ state.mTransferTouchFromToken = source.getToken();
state.register(display);
- return service.transferTouchFocus(source, state.getInputChannel());
+ return true;
}
/**
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index b4fe837..9ecafb3 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -119,6 +119,7 @@
"android.hardware.vibrator@1.0",
"android.hardware.vibrator@1.1",
"android.hardware.vibrator@1.2",
+ "android.hardware.vibrator@1.3",
"android.hardware.vr@1.0",
"android.frameworks.schedulerservice@1.0",
"android.frameworks.sensorservice@1.0",
diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp
index defcfd9..63dca62 100644
--- a/services/core/jni/com_android_server_VibratorService.cpp
+++ b/services/core/jni/com_android_server_VibratorService.cpp
@@ -22,6 +22,7 @@
#include <android/hardware/vibrator/1.1/types.h>
#include <android/hardware/vibrator/1.2/IVibrator.h>
#include <android/hardware/vibrator/1.2/types.h>
+#include <android/hardware/vibrator/1.3/IVibrator.h>
#include "jni.h"
#include <nativehelper/JNIHelp.h>
@@ -42,6 +43,7 @@
namespace V1_0 = android::hardware::vibrator::V1_0;
namespace V1_1 = android::hardware::vibrator::V1_1;
namespace V1_2 = android::hardware::vibrator::V1_2;
+namespace V1_3 = android::hardware::vibrator::V1_3;
namespace android {
@@ -136,6 +138,19 @@
}
}
+static jboolean vibratorSupportsExternalControl(JNIEnv*, jobject) {
+ return halCall(&V1_3::IVibrator::supportsExternalControl).withDefault(false);
+}
+
+static void vibratorSetExternalControl(JNIEnv*, jobject, jboolean enabled) {
+ Status status = halCall(&V1_3::IVibrator::setExternalControl, static_cast<uint32_t>(enabled))
+ .withDefault(Status::UNKNOWN_ERROR);
+ if (status != Status::OK) {
+ ALOGE("Failed to set vibrator external control (%" PRIu32 ").",
+ static_cast<uint32_t>(status));
+ }
+}
+
static jlong vibratorPerformEffect(JNIEnv*, jobject, jlong effect, jint strength) {
Status status;
uint32_t lengthMs;
@@ -187,7 +202,9 @@
{ "vibratorOff", "()V", (void*)vibratorOff },
{ "vibratorSupportsAmplitudeControl", "()Z", (void*)vibratorSupportsAmplitudeControl},
{ "vibratorSetAmplitude", "(I)V", (void*)vibratorSetAmplitude},
- { "vibratorPerformEffect", "(JJ)J", (void*)vibratorPerformEffect}
+ { "vibratorPerformEffect", "(JJ)J", (void*)vibratorPerformEffect},
+ { "vibratorSupportsExternalControl", "()Z", (void*)vibratorSupportsExternalControl},
+ { "vibratorSetExternalControl", "(Z)V", (void*)vibratorSetExternalControl},
};
int register_android_server_VibratorService(JNIEnv *env)
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 43d2dcf..0929e20 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -107,6 +107,7 @@
jmethodID getLongPressTimeout;
jmethodID getPointerLayer;
jmethodID getPointerIcon;
+ jmethodID getPointerDisplayId;
jmethodID getKeyboardLayoutOverlay;
jmethodID getDeviceAlias;
jmethodID getTouchCalibrationForInputDevice;
@@ -174,15 +175,6 @@
loadSystemIconAsSpriteWithPointerIcon(env, contextObj, style, &pointerIcon, outSpriteIcon);
}
-static void updatePointerControllerFromViewport(
- sp<PointerController> controller, const DisplayViewport* const viewport) {
- if (controller != nullptr && viewport != nullptr) {
- const int32_t width = viewport->logicalRight - viewport->logicalLeft;
- const int32_t height = viewport->logicalBottom - viewport->logicalTop;
- controller->setDisplayViewport(width, height, viewport->orientation);
- }
-}
-
enum {
WM_ACTION_PASS_TO_USER = 1,
};
@@ -310,14 +302,19 @@
// Input devices to be disabled
SortedVector<int32_t> disabledInputDevices;
+
+ // Associated Pointer controller display.
+ int32_t pointerDisplayId;
} mLocked GUARDED_BY(mLock);
std::atomic<bool> mInteractive;
- void updateInactivityTimeoutLocked(const sp<PointerController>& controller);
+ void updateInactivityTimeoutLocked();
void handleInterceptActions(jint wmActions, nsecs_t when, uint32_t& policyFlags);
void ensureSpriteControllerLocked();
-
+ const DisplayViewport* findDisplayViewportLocked(int32_t displayId);
+ int32_t getPointerDisplayId();
+ void updatePointerDisplayLocked();
static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
static inline JNIEnv* jniEnv() {
@@ -342,6 +339,7 @@
mLocked.pointerGesturesEnabled = true;
mLocked.showTouches = false;
mLocked.pointerCapture = false;
+ mLocked.pointerDisplayId = ADISPLAY_ID_DEFAULT;
}
mInteractive = true;
@@ -391,9 +389,10 @@
return false;
}
-static const DisplayViewport* findInternalViewport(const std::vector<DisplayViewport>& viewports) {
- for (const DisplayViewport& v : viewports) {
- if (v.type == ViewportType::VIEWPORT_INTERNAL) {
+const DisplayViewport* NativeInputManager::findDisplayViewportLocked(int32_t displayId)
+ REQUIRES(mLock) {
+ for (const DisplayViewport& v : mLocked.viewports) {
+ if (v.displayId == displayId) {
return &v;
}
}
@@ -420,20 +419,14 @@
}
}
- const DisplayViewport* newInternalViewport = findInternalViewport(viewports);
- {
+ // Get the preferred pointer controller displayId.
+ int32_t pointerDisplayId = getPointerDisplayId();
+
+ { // acquire lock
AutoMutex _l(mLock);
- const DisplayViewport* oldInternalViewport = findInternalViewport(mLocked.viewports);
- // Internal viewport has changed if there wasn't one earlier, and there is one now, or,
- // if they are different.
- const bool internalViewportChanged = (newInternalViewport != nullptr) &&
- (oldInternalViewport == nullptr || (*oldInternalViewport != *newInternalViewport));
- if (internalViewportChanged) {
- sp<PointerController> controller = mLocked.pointerController.promote();
- updatePointerControllerFromViewport(controller, newInternalViewport);
- }
mLocked.viewports = viewports;
- }
+ mLocked.pointerDisplayId = pointerDisplayId;
+ } // release lock
mInputManager->getReader()->requestRefreshConfiguration(
InputReaderConfiguration::CHANGE_DISPLAY_INFO);
@@ -556,15 +549,42 @@
controller = new PointerController(this, mLooper, mLocked.spriteController);
mLocked.pointerController = controller;
-
- const DisplayViewport* internalViewport = findInternalViewport(mLocked.viewports);
- updatePointerControllerFromViewport(controller, internalViewport);
-
- updateInactivityTimeoutLocked(controller);
+ updateInactivityTimeoutLocked();
}
+
+ updatePointerDisplayLocked();
+
return controller;
}
+int32_t NativeInputManager::getPointerDisplayId() {
+ JNIEnv* env = jniEnv();
+ jint pointerDisplayId = env->CallIntMethod(mServiceObj,
+ gServiceClassInfo.getPointerDisplayId);
+ if (checkAndClearExceptionFromCallback(env, "getPointerDisplayId")) {
+ pointerDisplayId = ADISPLAY_ID_DEFAULT;
+ }
+
+ return pointerDisplayId;
+}
+
+void NativeInputManager::updatePointerDisplayLocked() REQUIRES(mLock) {
+ ATRACE_CALL();
+
+ sp<PointerController> controller = mLocked.pointerController.promote();
+ if (controller != nullptr) {
+ const DisplayViewport* viewport = findDisplayViewportLocked(mLocked.pointerDisplayId);
+ if (viewport == nullptr) {
+ ALOGW("Can't find pointer display viewport, fallback to default display.");
+ viewport = findDisplayViewportLocked(ADISPLAY_ID_DEFAULT);
+ }
+
+ if (viewport != nullptr) {
+ controller->setDisplayViewport(*viewport);
+ }
+ }
+}
+
void NativeInputManager::ensureSpriteControllerLocked() REQUIRES(mLock) {
if (mLocked.spriteController == nullptr) {
JNIEnv* env = jniEnv();
@@ -821,16 +841,16 @@
if (mLocked.systemUiVisibility != visibility) {
mLocked.systemUiVisibility = visibility;
-
- sp<PointerController> controller = mLocked.pointerController.promote();
- if (controller != nullptr) {
- updateInactivityTimeoutLocked(controller);
- }
+ updateInactivityTimeoutLocked();
}
}
-void NativeInputManager::updateInactivityTimeoutLocked(const sp<PointerController>& controller)
- REQUIRES(mLock) {
+void NativeInputManager::updateInactivityTimeoutLocked() REQUIRES(mLock) {
+ sp<PointerController> controller = mLocked.pointerController.promote();
+ if (controller == nullptr) {
+ return;
+ }
+
bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN;
controller->setInactivityTimeout(lightsOut
? PointerController::INACTIVITY_TIMEOUT_SHORT
@@ -1824,6 +1844,9 @@
GET_METHOD_ID(gServiceClassInfo.getPointerIcon, clazz,
"getPointerIcon", "()Landroid/view/PointerIcon;");
+ GET_METHOD_ID(gServiceClassInfo.getPointerDisplayId, clazz,
+ "getPointerDisplayId", "()I");
+
GET_METHOD_ID(gServiceClassInfo.getKeyboardLayoutOverlay, clazz,
"getKeyboardLayoutOverlay",
"(Landroid/hardware/input/InputDeviceIdentifier;)[Ljava/lang/String;");
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 6d8fc1c..b290bc5 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -135,6 +135,7 @@
using IGnss_V2_0 = android::hardware::gnss::V2_0::IGnss;
using IGnssConfiguration_V1_0 = android::hardware::gnss::V1_0::IGnssConfiguration;
using IGnssConfiguration_V1_1 = android::hardware::gnss::V1_1::IGnssConfiguration;
+using IGnssConfiguration_V2_0 = android::hardware::gnss::V2_0::IGnssConfiguration;
using IGnssMeasurement_V1_0 = android::hardware::gnss::V1_0::IGnssMeasurement;
using IGnssMeasurement_V1_1 = android::hardware::gnss::V1_1::IGnssMeasurement;
using IGnssMeasurement_V2_0 = android::hardware::gnss::V2_0::IGnssMeasurement;
@@ -179,6 +180,7 @@
sp<IGnssDebug> gnssDebugIface = nullptr;
sp<IGnssConfiguration_V1_0> gnssConfigurationIface = nullptr;
sp<IGnssConfiguration_V1_1> gnssConfigurationIface_V1_1 = nullptr;
+sp<IGnssConfiguration_V2_0> gnssConfigurationIface_V2_0 = nullptr;
sp<IGnssNi> gnssNiIface = nullptr;
sp<IGnssMeasurement_V1_0> gnssMeasurementIface = nullptr;
sp<IGnssMeasurement_V1_1> gnssMeasurementIface_V1_1 = nullptr;
@@ -313,6 +315,15 @@
}
}
+static jobject createHalInterfaceVersionJavaObject(JNIEnv* env, jint major, jint minor) {
+ jclass versionClass =
+ env->FindClass("com/android/server/location/GnssConfiguration$HalInterfaceVersion");
+ jmethodID versionCtor = env->GetMethodID(versionClass, "<init>", "(II)V");
+ jobject version = env->NewObject(versionClass, versionCtor, major, minor);
+ env->DeleteLocalRef(versionClass);
+ return version;
+}
+
struct ScopedJniString {
ScopedJniString(JNIEnv* env, jstring javaString) : mEnv(env), mJavaString(javaString) {
mNativeString = mEnv->GetStringUTFChars(mJavaString, nullptr);
@@ -1421,10 +1432,19 @@
gnssNiIface = gnssNi;
}
- if (gnssHal_V1_1 != nullptr) {
+ if (gnssHal_V2_0 != nullptr) {
+ auto gnssConfiguration = gnssHal_V2_0->getExtensionGnssConfiguration_2_0();
+ if (!gnssConfiguration.isOk()) {
+ ALOGD("Unable to get a handle to GnssConfiguration_V2_0");
+ } else {
+ gnssConfigurationIface_V2_0 = gnssConfiguration;
+ gnssConfigurationIface_V1_1 = gnssConfigurationIface_V2_0;
+ gnssConfigurationIface = gnssConfigurationIface_V2_0;
+ }
+ } else if (gnssHal_V1_1 != nullptr) {
auto gnssConfiguration = gnssHal_V1_1->getExtensionGnssConfiguration_1_1();
if (!gnssConfiguration.isOk()) {
- ALOGD("Unable to get a handle to GnssConfiguration");
+ ALOGD("Unable to get a handle to GnssConfiguration_V1_1");
} else {
gnssConfigurationIface_V1_1 = gnssConfiguration;
gnssConfigurationIface = gnssConfigurationIface_V1_1;
@@ -1463,9 +1483,23 @@
return (agnssRilIface != nullptr) ? JNI_TRUE : JNI_FALSE;
}
-static jboolean android_location_gpsLocationProvider_is_gnss_configuration_supported(
- JNIEnv* /* env */, jclass /* jclazz */) {
- return (gnssConfigurationIface != nullptr) ? JNI_TRUE : JNI_FALSE;
+static jobject android_location_GnssConfiguration_get_gnss_configuration_version(
+ JNIEnv* env, jclass /* jclazz */) {
+ jint major, minor;
+ if (gnssConfigurationIface_V2_0 != nullptr) {
+ major = 2;
+ minor = 0;
+ } else if (gnssConfigurationIface_V1_1 != nullptr) {
+ major = 1;
+ minor = 1;
+ } else if (gnssConfigurationIface != nullptr) {
+ major = 1;
+ minor = 0;
+ } else {
+ return nullptr;
+ }
+
+ return createHalInterfaceVersionJavaObject(env, major, minor);
}
static jboolean android_location_GnssLocationProvider_init(JNIEnv* env, jobject obj) {
@@ -2278,9 +2312,9 @@
return boolToJbool(result.isOk());
}
-static jboolean android_location_GnssLocationProvider_set_emergency_supl_pdn(JNIEnv*,
- jobject,
- jint emergencySuplPdn) {
+static jboolean android_location_GnssConfiguration_set_emergency_supl_pdn(JNIEnv*,
+ jobject,
+ jint emergencySuplPdn) {
if (gnssConfigurationIface == nullptr) {
ALOGE("no GNSS configuration interface available");
return JNI_FALSE;
@@ -2294,7 +2328,7 @@
}
}
-static jboolean android_location_GnssLocationProvider_set_supl_version(JNIEnv*,
+static jboolean android_location_GnssConfiguration_set_supl_version(JNIEnv*,
jobject,
jint version) {
if (gnssConfigurationIface == nullptr) {
@@ -2309,9 +2343,14 @@
}
}
-static jboolean android_location_GnssLocationProvider_set_supl_es(JNIEnv*,
- jobject,
- jint suplEs) {
+static jboolean android_location_GnssConfiguration_set_supl_es(JNIEnv*,
+ jobject,
+ jint suplEs) {
+ if (gnssConfigurationIface_V2_0 != nullptr) {
+ ALOGI("Config parameter SUPL_ES is deprecated in IGnssConfiguration.hal version 2.0.");
+ return JNI_FALSE;
+ }
+
if (gnssConfigurationIface == nullptr) {
ALOGE("no GNSS configuration interface available");
return JNI_FALSE;
@@ -2325,9 +2364,9 @@
}
}
-static jboolean android_location_GnssLocationProvider_set_supl_mode(JNIEnv*,
- jobject,
- jint mode) {
+static jboolean android_location_GnssConfiguration_set_supl_mode(JNIEnv*,
+ jobject,
+ jint mode) {
if (gnssConfigurationIface == nullptr) {
ALOGE("no GNSS configuration interface available");
return JNI_FALSE;
@@ -2341,9 +2380,14 @@
}
}
-static jboolean android_location_GnssLocationProvider_set_gps_lock(JNIEnv*,
- jobject,
- jint gpsLock) {
+static jboolean android_location_GnssConfiguration_set_gps_lock(JNIEnv*,
+ jobject,
+ jint gpsLock) {
+ if (gnssConfigurationIface_V2_0 != nullptr) {
+ ALOGI("Config parameter GPS_LOCK is deprecated in IGnssConfiguration.hal version 2.0.");
+ return JNI_FALSE;
+ }
+
if (gnssConfigurationIface == nullptr) {
ALOGE("no GNSS configuration interface available");
return JNI_FALSE;
@@ -2357,7 +2401,7 @@
}
}
-static jboolean android_location_GnssLocationProvider_set_lpp_profile(JNIEnv*,
+static jboolean android_location_GnssConfiguration_set_lpp_profile(JNIEnv*,
jobject,
jint lppProfile) {
if (gnssConfigurationIface == nullptr) {
@@ -2374,9 +2418,9 @@
}
}
-static jboolean android_location_GnssLocationProvider_set_gnss_pos_protocol_select(JNIEnv*,
- jobject,
- jint gnssPosProtocol) {
+static jboolean android_location_GnssConfiguration_set_gnss_pos_protocol_select(JNIEnv*,
+ jobject,
+ jint gnssPosProtocol) {
if (gnssConfigurationIface == nullptr) {
ALOGE("no GNSS configuration interface available");
return JNI_FALSE;
@@ -2390,7 +2434,7 @@
}
}
-static jboolean android_location_GnssLocationProvider_set_satellite_blacklist(
+static jboolean android_location_GnssConfiguration_set_satellite_blacklist(
JNIEnv* env, jobject, jintArray constellations, jintArray sv_ids) {
if (gnssConfigurationIface_V1_1 == nullptr) {
ALOGI("No GNSS Satellite Blacklist interface available");
@@ -2431,6 +2475,27 @@
}
}
+static jboolean android_location_GnssConfiguration_set_es_extension_sec(
+ JNIEnv*, jobject, jint emergencyExtensionSeconds) {
+ if (gnssConfigurationIface == nullptr) {
+ ALOGE("no GNSS configuration interface available");
+ return JNI_FALSE;
+ }
+
+ if (gnssConfigurationIface_V2_0 == nullptr) {
+ ALOGI("Config parameter ES_EXTENSION_SEC is not supported in IGnssConfiguration.hal"
+ " versions earlier than 2.0.");
+ return JNI_FALSE;
+ }
+
+ auto result = gnssConfigurationIface_V2_0->setEsExtensionSec(emergencyExtensionSeconds);
+ if (result.isOk()) {
+ return result;
+ } else {
+ return JNI_FALSE;
+ }
+}
+
static jint android_location_GnssBatchingProvider_get_batch_size(JNIEnv*, jclass) {
if (gnssBatchingIface == nullptr) {
return 0; // batching not supported, size = 0
@@ -2498,17 +2563,14 @@
android_location_GnssLocationProvider_class_init_native)},
{"native_is_supported", "()Z", reinterpret_cast<void *>(
android_location_GnssLocationProvider_is_supported)},
- {"native_is_gnss_configuration_supported", "()Z",
- reinterpret_cast<void *>(
- android_location_gpsLocationProvider_is_gnss_configuration_supported)},
{"native_init_once", "()V", reinterpret_cast<void *>(
android_location_GnssLocationProvider_init_once)},
{"native_init", "()Z", reinterpret_cast<void *>(android_location_GnssLocationProvider_init)},
{"native_cleanup", "()V", reinterpret_cast<void *>(
android_location_GnssLocationProvider_cleanup)},
{"native_set_position_mode",
- "(IIIIIZ)Z",
- reinterpret_cast<void*>(android_location_GnssLocationProvider_set_position_mode)},
+ "(IIIIIZ)Z",
+ reinterpret_cast<void*>(android_location_GnssLocationProvider_set_position_mode)},
{"native_start", "()Z", reinterpret_cast<void*>(android_location_GnssLocationProvider_start)},
{"native_stop", "()Z", reinterpret_cast<void*>(android_location_GnssLocationProvider_stop)},
{"native_delete_aiding_data",
@@ -2545,31 +2607,6 @@
{"native_get_internal_state",
"()Ljava/lang/String;",
reinterpret_cast<void *>(android_location_GnssLocationProvider_get_internal_state)},
- {"native_set_supl_es",
- "(I)Z",
- reinterpret_cast<void *>(android_location_GnssLocationProvider_set_supl_es)},
- {"native_set_supl_version",
- "(I)Z",
- reinterpret_cast<void *>(android_location_GnssLocationProvider_set_supl_version)},
- {"native_set_supl_mode",
- "(I)Z",
- reinterpret_cast<void *>(android_location_GnssLocationProvider_set_supl_mode)},
- {"native_set_lpp_profile",
- "(I)Z",
- reinterpret_cast<void *>(android_location_GnssLocationProvider_set_lpp_profile)},
- {"native_set_gnss_pos_protocol_select",
- "(I)Z",
- reinterpret_cast<void *>(
- android_location_GnssLocationProvider_set_gnss_pos_protocol_select)},
- {"native_set_gps_lock",
- "(I)Z",
- reinterpret_cast<void *>(android_location_GnssLocationProvider_set_gps_lock)},
- {"native_set_emergency_supl_pdn",
- "(I)Z",
- reinterpret_cast<void *>(android_location_GnssLocationProvider_set_emergency_supl_pdn)},
- {"native_set_satellite_blacklist",
- "([I[I)Z",
- reinterpret_cast<void *>(android_location_GnssLocationProvider_set_satellite_blacklist)},
};
static const JNINativeMethod sMethodsBatching[] = {
@@ -2663,6 +2700,42 @@
reinterpret_cast<void *>(android_location_GnssNetworkConnectivityHandler_agps_data_conn_failed)},
};
+static const JNINativeMethod sConfigurationMethods[] = {
+ /* name, signature, funcPtr */
+ {"native_get_gnss_configuration_version",
+ "()Lcom/android/server/location/GnssConfiguration$HalInterfaceVersion;",
+ reinterpret_cast<void *>(
+ android_location_GnssConfiguration_get_gnss_configuration_version)},
+ {"native_set_supl_es",
+ "(I)Z",
+ reinterpret_cast<void *>(android_location_GnssConfiguration_set_supl_es)},
+ {"native_set_supl_version",
+ "(I)Z",
+ reinterpret_cast<void *>(android_location_GnssConfiguration_set_supl_version)},
+ {"native_set_supl_mode",
+ "(I)Z",
+ reinterpret_cast<void *>(android_location_GnssConfiguration_set_supl_mode)},
+ {"native_set_lpp_profile",
+ "(I)Z",
+ reinterpret_cast<void *>(android_location_GnssConfiguration_set_lpp_profile)},
+ {"native_set_gnss_pos_protocol_select",
+ "(I)Z",
+ reinterpret_cast<void *>(
+ android_location_GnssConfiguration_set_gnss_pos_protocol_select)},
+ {"native_set_gps_lock",
+ "(I)Z",
+ reinterpret_cast<void *>(android_location_GnssConfiguration_set_gps_lock)},
+ {"native_set_emergency_supl_pdn",
+ "(I)Z",
+ reinterpret_cast<void *>(android_location_GnssConfiguration_set_emergency_supl_pdn)},
+ {"native_set_satellite_blacklist",
+ "([I[I)Z",
+ reinterpret_cast<void *>(android_location_GnssConfiguration_set_satellite_blacklist)},
+ {"native_set_es_extension_sec",
+ "(I)Z",
+ reinterpret_cast<void *>(android_location_GnssConfiguration_set_es_extension_sec)},
+};
+
int register_android_server_location_GnssLocationProvider(JNIEnv* env) {
jniRegisterNativeMethods(
env,
@@ -2689,6 +2762,11 @@
"com/android/server/location/GnssNetworkConnectivityHandler",
sNetworkConnectivityMethods,
NELEM(sNetworkConnectivityMethods));
+ jniRegisterNativeMethods(
+ env,
+ "com/android/server/location/GnssConfiguration",
+ sConfigurationMethods,
+ NELEM(sConfigurationMethods));
return jniRegisterNativeMethods(
env,
"com/android/server/location/GnssLocationProvider",
diff --git a/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java b/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
index 423512c..aca48b6 100644
--- a/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
@@ -12,6 +12,7 @@
import static org.testng.Assert.expectThrows;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.Application;
import android.app.backup.BackupDataInput;
import android.app.backup.FullBackupDataOutput;
@@ -62,6 +63,7 @@
private static final String TEST_PACKAGE_INSTALLER = "com.test.package.installer";
private static final Long TEST_PACKAGE_VERSION_CODE = 100L;
+ private @UserIdInt int mUserId;
private PackageManager mPackageManager;
private ShadowApplicationPackageManager mShadowPackageManager;
private File mFilesDir;
@@ -72,6 +74,7 @@
public void setUp() throws Exception {
Application application = RuntimeEnvironment.application;
+ mUserId = UserHandle.USER_SYSTEM;
mPackageManager = application.getPackageManager();
mShadowPackageManager = (ShadowApplicationPackageManager) shadowOf(mPackageManager);
@@ -352,7 +355,7 @@
byte[] obbBytes = "obb".getBytes();
File obbFile = createObbFileAndWrite(obbDir, obbBytes);
- mBackupWriter.backupObb(packageInfo);
+ mBackupWriter.backupObb(mUserId, packageInfo);
byte[] writtenBytes = getWrittenBytes(mBackupDataOutputFile, /* includeTarHeader */ false);
assertThat(writtenBytes).isEqualTo(obbBytes);
@@ -366,7 +369,7 @@
File obbDir = createObbDirForPackage(packageInfo.packageName);
// No obb file created.
- mBackupWriter.backupObb(packageInfo);
+ mBackupWriter.backupObb(mUserId, packageInfo);
assertThat(mBackupDataOutputFile.length()).isEqualTo(0);
}
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index 5e84ab0..7dac795 100644
--- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -71,6 +71,7 @@
import android.annotation.Nullable;
import android.app.Application;
+import android.app.ApplicationPackageManager;
import android.app.IBackupAgent;
import android.app.backup.BackupAgent;
import android.app.backup.BackupDataInput;
@@ -134,6 +135,8 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.Resetter;
import org.robolectric.shadows.ShadowLooper;
import org.robolectric.shadows.ShadowPackageManager;
import org.robolectric.shadows.ShadowQueuedWork;
@@ -157,6 +160,7 @@
@Config(
shadows = {
FrameworkShadowLooper.class,
+ KeyValueBackupTaskTest.ShadowApplicationPackageManager.class,
ShadowBackupDataInput.class,
ShadowBackupDataOutput.class,
ShadowEventLog.class,
@@ -244,6 +248,7 @@
@After
public void tearDown() throws Exception {
ShadowBackupDataInput.reset();
+ ShadowApplicationPackageManager.reset();
}
@Test
@@ -2435,7 +2440,8 @@
mPackageManager.setApplicationEnabledSetting(
packageData.packageName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
PackageInfo packageInfo = getPackageInfo(packageData);
- mShadowPackageManager.addPackage(packageInfo);
+ mShadowPackageManager.installPackage(packageInfo);
+ ShadowApplicationPackageManager.setPackageInfo(packageInfo);
mContext.sendBroadcast(getPackageAddedIntent(packageData));
// Run the backup looper because on the receiver we post MSG_SCHEDULE_BACKUP_PACKAGE
mShadowBackupLooper.runToEndOfTasks();
@@ -2848,4 +2854,29 @@
throw mException;
}
}
+
+ /**
+ * Extends {@link org.robolectric.shadows.ShadowApplicationPackageManager} to return the correct
+ * package in user-specific invocations.
+ */
+ @Implements(value = ApplicationPackageManager.class)
+ public static class ShadowApplicationPackageManager
+ extends org.robolectric.shadows.ShadowApplicationPackageManager {
+ private static PackageInfo sPackageInfo;
+
+ static void setPackageInfo(PackageInfo packageInfo) {
+ sPackageInfo = packageInfo;
+ }
+
+ @Override
+ protected PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId) {
+ return sPackageInfo;
+ }
+
+ /** Clear {@link #sPackageInfo}. */
+ @Resetter
+ public static void reset() {
+ sPackageInfo = null;
+ }
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index ee336a8..bd29d2a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -169,6 +169,7 @@
private final ActivityTaskManagerService mService;
private ComponentName mComponent;
+ private String mTargetActivity;
private TaskRecord mTaskRecord;
private int mUid;
private boolean mCreateTask;
@@ -185,6 +186,11 @@
return this;
}
+ ActivityBuilder setTargetActivity(String targetActivity) {
+ mTargetActivity = targetActivity;
+ return this;
+ }
+
static ComponentName getDefaultComponent() {
return ComponentName.createRelative(DEFAULT_COMPONENT_PACKAGE_NAME,
DEFAULT_COMPONENT_PACKAGE_NAME);
@@ -239,6 +245,10 @@
aInfo.applicationInfo = new ApplicationInfo();
aInfo.applicationInfo.packageName = mComponent.getPackageName();
aInfo.applicationInfo.uid = mUid;
+ aInfo.packageName = mComponent.getPackageName();
+ if (mTargetActivity != null) {
+ aInfo.targetActivity = mTargetActivity;
+ }
aInfo.flags |= mActivityFlags;
aInfo.launchMode = mLaunchMode;
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
index dcfb879..d0b9225 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
@@ -87,8 +87,8 @@
verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture());
callbackCaptor.getValue().onAnimationFinished(mSpec);
- verify(mTransaction).destroy(eq(leash));
- verify(mTransaction).destroy(eq(animationBoundsLayer));
+ verify(mTransaction).reparent(eq(leash), eq(null));
+ verify(mTransaction).reparent(eq(animationBoundsLayer), eq(null));
assertThat(mToken.mNeedsAnimationBoundsLayer).isFalse();
}
@@ -100,8 +100,8 @@
final SurfaceControl animationBoundsLayer = mToken.mAnimationBoundsLayer;
mToken.mSurfaceAnimator.cancelAnimation();
- verify(mTransaction).destroy(eq(leash));
- verify(mTransaction).destroy(eq(animationBoundsLayer));
+ verify(mTransaction).reparent(eq(leash), eq(null));
+ verify(mTransaction).reparent(eq(animationBoundsLayer), eq(null));
assertThat(mToken.mNeedsAnimationBoundsLayer).isFalse();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index ee1c8df..f99cd4b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -212,7 +212,7 @@
mDimmer.updateDims(mTransaction, new Rect());
verify(mSurfaceAnimatorStarter).startAnimation(any(SurfaceAnimator.class), any(
SurfaceControl.Transaction.class), any(AnimationAdapter.class), anyBoolean());
- verify(mHost.getPendingTransaction()).destroy(dimLayer);
+ verify(mHost.getPendingTransaction()).reparent(dimLayer, null);
}
@Test
@@ -269,7 +269,7 @@
mDimmer.updateDims(mTransaction, new Rect());
verify(mSurfaceAnimatorStarter, never()).startAnimation(any(SurfaceAnimator.class), any(
SurfaceControl.Transaction.class), any(AnimationAdapter.class), anyBoolean());
- verify(mTransaction).destroy(dimLayer);
+ verify(mTransaction).reparent(dimLayer, null);
}
private SurfaceControl getDimLayer() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
index d14f30d..ad80cd6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -96,7 +96,7 @@
callbackCaptor.getValue().onAnimationFinished(mSpec);
assertNotAnimating(mAnimatable);
assertTrue(mAnimatable.mFinishedCallbackCalled);
- verify(mTransaction).destroy(eq(mAnimatable.mLeash));
+ verify(mTransaction).reparent(eq(mAnimatable.mLeash), eq(null));
// TODO: Verify reparenting once we use mPendingTransaction to reparent it back
}
@@ -106,7 +106,7 @@
final SurfaceControl firstLeash = mAnimatable.mLeash;
mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec2, true /* hidden */);
- verify(mTransaction).destroy(eq(firstLeash));
+ verify(mTransaction).reparent(eq(firstLeash), eq(null));
assertFalse(mAnimatable.mFinishedCallbackCalled);
final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
@@ -133,7 +133,7 @@
assertNotAnimating(mAnimatable);
verify(mSpec).onAnimationCancelled(any());
assertTrue(mAnimatable.mFinishedCallbackCalled);
- verify(mTransaction).destroy(eq(mAnimatable.mLeash));
+ verify(mTransaction).reparent(eq(mAnimatable.mLeash), eq(null));
}
@Test
@@ -155,7 +155,7 @@
verifyZeroInteractions(mSpec);
assertNotAnimating(mAnimatable);
assertTrue(mAnimatable.mFinishedCallbackCalled);
- verify(mTransaction).destroy(eq(mAnimatable.mLeash));
+ verify(mTransaction).reparent(eq(mAnimatable.mLeash), eq(null));
}
@Test
@@ -171,11 +171,11 @@
assertNotAnimating(mAnimatable);
assertAnimating(mAnimatable2);
assertEquals(leash, mAnimatable2.mSurfaceAnimator.mLeash);
- verify(mTransaction, never()).destroy(eq(leash));
+ verify(mTransaction, never()).reparent(eq(leash), eq(null));
callbackCaptor.getValue().onAnimationFinished(mSpec);
assertNotAnimating(mAnimatable2);
assertTrue(mAnimatable2.mFinishedCallbackCalled);
- verify(mTransaction).destroy(eq(leash));
+ verify(mTransaction).reparent(eq(leash), eq(null));
}
@Test
@@ -198,7 +198,7 @@
mDeferFinishAnimatable.mEndDeferFinishCallback.run();
assertNotAnimating(mAnimatable2);
assertTrue(mDeferFinishAnimatable.mFinishedCallbackCalled);
- verify(mTransaction).destroy(eq(mDeferFinishAnimatable.mLeash));
+ verify(mTransaction).reparent(eq(mDeferFinishAnimatable.mLeash), eq(null));
}
private void assertAnimating(MyAnimatable animatable) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index 00bec3f..8a98cbe 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -268,19 +268,38 @@
/** Ensures that the alias intent won't have target component resolved. */
@Test
public void testTaskIntentActivityAlias() {
- final String aliasActivity = DEFAULT_COMPONENT_PACKAGE_NAME + ".aliasActivity";
- final String targetActivity = DEFAULT_COMPONENT_PACKAGE_NAME + ".targetActivity";
+ final String aliasClassName = DEFAULT_COMPONENT_PACKAGE_NAME + ".aliasActivity";
+ final String targetClassName = DEFAULT_COMPONENT_PACKAGE_NAME + ".targetActivity";
+ final ComponentName aliasComponent =
+ new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, aliasClassName);
+ final ComponentName targetComponent =
+ new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, targetClassName);
+
final Intent intent = new Intent();
- intent.setComponent(new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, aliasActivity));
+ intent.setComponent(aliasComponent);
final ActivityInfo info = new ActivityInfo();
info.applicationInfo = new ApplicationInfo();
info.packageName = DEFAULT_COMPONENT_PACKAGE_NAME;
- info.targetActivity = targetActivity;
+ info.targetActivity = targetClassName;
final TaskRecord task = TaskRecord.create(mService, 1 /* taskId */, info, intent,
null /* taskDescription */);
- assertEquals("The alias activity component should be saved in task intent.", aliasActivity,
+ assertEquals("The alias activity component should be saved in task intent.", aliasClassName,
task.intent.getComponent().getClassName());
+
+ ActivityRecord aliasActivity = new ActivityBuilder(mService).setComponent(
+ aliasComponent).setTargetActivity(targetClassName).build();
+ assertEquals("Should be the same intent filter.", true,
+ task.isSameIntentFilter(aliasActivity));
+
+ ActivityRecord targetActivity = new ActivityBuilder(mService).setComponent(
+ targetComponent).build();
+ assertEquals("Should be the same intent filter.", true,
+ task.isSameIntentFilter(targetActivity));
+
+ ActivityRecord defaultActivity = new ActivityBuilder(mService).build();
+ assertEquals("Should not be the same intent filter.", false,
+ task.isSameIntentFilter(defaultActivity));
}
private void testStackBoundsConfiguration(int windowingMode, Rect parentBounds, Rect bounds,
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index 38efc74..3d7cbb5 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -657,9 +657,8 @@
return;
}
- if (event.status != SoundTrigger.RECOGNITION_STATUS_GET_STATE_RESPONSE) {
- model.setStopped();
- }
+ model.setStopped();
+
try {
callback.onGenericSoundTriggerDetected((GenericRecognitionEvent) event);
} catch (DeadObjectException e) {
@@ -802,9 +801,7 @@
return;
}
- if (event.status != SoundTrigger.RECOGNITION_STATUS_GET_STATE_RESPONSE) {
- modelData.setStopped();
- }
+ modelData.setStopped();
try {
modelData.getCallback().onKeyphraseDetected((KeyphraseRecognitionEvent) event);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 6402ec4..6484005 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -5858,9 +5858,14 @@
/**
* Returns the IMS Service Table (IST) that was loaded from the ISIM.
+ *
+ * See 3GPP TS 31.103 (Section 4.2.7) for the definition and more information on this table.
+ *
* @return IMS Service Table or null if not present or not loaded
* @hide
*/
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public String getIsimIst() {
try {
IPhoneSubInfo info = getSubscriberInfo();
diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java
index fe062d5..a94b163 100644
--- a/telephony/java/android/telephony/emergency/EmergencyNumber.java
+++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java
@@ -29,6 +29,7 @@
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
/**
@@ -198,21 +199,53 @@
EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_DEFAULT);
}
+ /**
+ * Indicated the framework does not know whether an emergency call should be placed using
+ * emergency or normal call routing. This means the underlying radio or IMS implementation is
+ * free to determine for itself how to route the call.
+ */
+ public static final int EMERGENCY_CALL_ROUTING_UNKNOWN = 0;
+ /**
+ * Indicates the radio or IMS implementation must handle the call through emergency routing.
+ */
+ public static final int EMERGENCY_CALL_ROUTING_EMERGENCY = 1;
+ /**
+ * Indicates the radio or IMS implementation must handle the call through normal call routing.
+ */
+ public static final int EMERGENCY_CALL_ROUTING_NORMAL = 2;
+
+ /**
+ * The routing to tell how to handle the call for the corresponding emergency number.
+ *
+ * @hide
+ */
+ @IntDef(flag = false, prefix = { "EMERGENCY_CALL_ROUTING_" }, value = {
+ EMERGENCY_CALL_ROUTING_UNKNOWN,
+ EMERGENCY_CALL_ROUTING_EMERGENCY,
+ EMERGENCY_CALL_ROUTING_NORMAL
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EmergencyCallRouting {}
+
+
private final String mNumber;
private final String mCountryIso;
private final String mMnc;
private final int mEmergencyServiceCategoryBitmask;
private final int mEmergencyNumberSourceBitmask;
+ private final int mEmergencyCallRouting;
/** @hide */
- public EmergencyNumber(@NonNull String number, @NonNull String countryIso,
- @NonNull String mnc, int emergencyServiceCategories,
- int emergencyNumberSources) {
+ public EmergencyNumber(@NonNull String number, @NonNull String countryIso, @NonNull String mnc,
+ @EmergencyServiceCategories int emergencyServiceCategories,
+ @EmergencyNumberSources int emergencyNumberSources,
+ @EmergencyCallRouting int emergencyCallRouting) {
this.mNumber = number;
this.mCountryIso = countryIso;
this.mMnc = mnc;
this.mEmergencyServiceCategoryBitmask = emergencyServiceCategories;
this.mEmergencyNumberSourceBitmask = emergencyNumberSources;
+ this.mEmergencyCallRouting = emergencyCallRouting;
}
/** @hide */
@@ -222,8 +255,33 @@
mMnc = source.readString();
mEmergencyServiceCategoryBitmask = source.readInt();
mEmergencyNumberSourceBitmask = source.readInt();
+ mEmergencyCallRouting = source.readInt();
}
+ @Override
+ /** @hide */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mNumber);
+ dest.writeString(mCountryIso);
+ dest.writeString(mMnc);
+ dest.writeInt(mEmergencyServiceCategoryBitmask);
+ dest.writeInt(mEmergencyNumberSourceBitmask);
+ dest.writeInt(mEmergencyCallRouting);
+ }
+
+ public static final Parcelable.Creator<EmergencyNumber> CREATOR =
+ new Parcelable.Creator<EmergencyNumber>() {
+ @Override
+ public EmergencyNumber createFromParcel(Parcel in) {
+ return new EmergencyNumber(in);
+ }
+
+ @Override
+ public EmergencyNumber[] newArray(int size) {
+ return new EmergencyNumber[size];
+ }
+ };
+
/**
* Get the dialing number of the emergency number.
*
@@ -352,14 +410,17 @@
return (mEmergencyNumberSourceBitmask & sources) == sources;
}
- @Override
- /** @hide */
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(mNumber);
- dest.writeString(mCountryIso);
- dest.writeString(mMnc);
- dest.writeInt(mEmergencyServiceCategoryBitmask);
- dest.writeInt(mEmergencyNumberSourceBitmask);
+ /**
+ * Returns the emergency call routing information.
+ *
+ * <p>Some regions require some emergency numbers which are not routed using typical emergency
+ * call processing, but are instead placed as regular phone calls. The emergency call routing
+ * field provides information about how an emergency call will be routed when it is placed.
+ *
+ * @return the emergency call routing requirement
+ */
+ public @EmergencyCallRouting int getEmergencyCallRouting() {
+ return mEmergencyCallRouting;
}
@Override
@@ -373,7 +434,8 @@
return "EmergencyNumber:" + "Number-" + mNumber + "|CountryIso-" + mCountryIso
+ "|Mnc-" + mMnc
+ "|ServiceCategories-" + Integer.toBinaryString(mEmergencyServiceCategoryBitmask)
- + "|Sources-" + Integer.toBinaryString(mEmergencyNumberSourceBitmask);
+ + "|Sources-" + Integer.toBinaryString(mEmergencyNumberSourceBitmask)
+ + "|Routing-" + Integer.toBinaryString(mEmergencyCallRouting);
}
@Override
@@ -381,7 +443,19 @@
if (!EmergencyNumber.class.isInstance(o)) {
return false;
}
- return (o == this || toString().equals(o.toString()));
+ EmergencyNumber other = (EmergencyNumber) o;
+ return mNumber.equals(other.mNumber)
+ && mCountryIso.equals(other.mCountryIso)
+ && mMnc.equals(other.mMnc)
+ && mEmergencyServiceCategoryBitmask == other.mEmergencyServiceCategoryBitmask
+ && mEmergencyNumberSourceBitmask == other.mEmergencyNumberSourceBitmask
+ && mEmergencyCallRouting == other.mEmergencyCallRouting;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mNumber, mCountryIso, mMnc, mEmergencyServiceCategoryBitmask,
+ mEmergencyNumberSourceBitmask, mEmergencyCallRouting);
}
/**
@@ -462,12 +536,12 @@
continue;
}
for (int j = i + 1; j < emergencyNumberList.size(); j++) {
- if (isSameEmergencyNumber(
+ if (areSameEmergencyNumbers(
emergencyNumberList.get(i), emergencyNumberList.get(j))) {
Rlog.e(LOG_TAG, "Found unexpected duplicate numbers: "
+ emergencyNumberList.get(i) + " vs " + emergencyNumberList.get(j));
// Set the merged emergency number in the current position
- emergencyNumberList.set(i, mergeNumbers(
+ emergencyNumberList.set(i, mergeSameEmergencyNumbers(
emergencyNumberList.get(i), emergencyNumberList.get(j)));
// Mark the emergency number has been merged
mergedEmergencyNumber.add(emergencyNumberList.get(j));
@@ -486,8 +560,8 @@
* Check if two emergency numbers are the same.
*
* A unique EmergencyNumber has a unique combination of ‘number’, ‘mcc’, 'mnc' and
- * 'categories' fields. Multiple Emergency Number Sources should be merged into one bitfield
- * for the same EmergencyNumber.
+ * 'categories', and 'routing' fields. Multiple Emergency Number Sources should be
+ * merged into one bitfield for the same EmergencyNumber.
*
* @param first first EmergencyNumber to compare
* @param second second EmergencyNumber to compare
@@ -495,8 +569,8 @@
*
* @hide
*/
- public static boolean isSameEmergencyNumber(@NonNull EmergencyNumber first,
- @NonNull EmergencyNumber second) {
+ public static boolean areSameEmergencyNumbers(@NonNull EmergencyNumber first,
+ @NonNull EmergencyNumber second) {
if (!first.getNumber().equals(second.getNumber())) {
return false;
}
@@ -510,11 +584,15 @@
!= second.getEmergencyServiceCategoryBitmask()) {
return false;
}
+ if (first.getEmergencyCallRouting() != second.getEmergencyCallRouting()) {
+ return false;
+ }
return true;
}
/**
- * Get a merged EmergencyNumber for two numbers if they are the same.
+ * Get a merged EmergencyNumber from two same emergency numbers. Two emergency numbers are
+ * the same if {@link #areSameEmergencyNumbers} returns {@code true}.
*
* @param first first EmergencyNumber to compare
* @param second second EmergencyNumber to compare
@@ -522,27 +600,15 @@
*
* @hide
*/
- public static EmergencyNumber mergeNumbers(@NonNull EmergencyNumber first,
- @NonNull EmergencyNumber second) {
- if (isSameEmergencyNumber(first, second)) {
+ public static EmergencyNumber mergeSameEmergencyNumbers(@NonNull EmergencyNumber first,
+ @NonNull EmergencyNumber second) {
+ if (areSameEmergencyNumbers(first, second)) {
return new EmergencyNumber(first.getNumber(), first.getCountryIso(), first.getMnc(),
first.getEmergencyServiceCategoryBitmask(),
first.getEmergencyNumberSourceBitmask()
- | second.getEmergencyNumberSourceBitmask());
+ | second.getEmergencyNumberSourceBitmask(),
+ first.getEmergencyCallRouting());
}
return null;
}
-
- public static final Parcelable.Creator<EmergencyNumber> CREATOR =
- new Parcelable.Creator<EmergencyNumber>() {
- @Override
- public EmergencyNumber createFromParcel(Parcel in) {
- return new EmergencyNumber(in);
- }
-
- @Override
- public EmergencyNumber[] newArray(int size) {
- return new EmergencyNumber[size];
- }
- };
}
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index cb6fcd7..9c8d078 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -24,9 +24,11 @@
import android.os.Parcelable;
import android.telecom.VideoProfile;
import android.telephony.emergency.EmergencyNumber;
+import android.telephony.emergency.EmergencyNumber.EmergencyCallRouting;
import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.PhoneConstants;
import java.lang.annotation.Retention;
@@ -321,6 +323,20 @@
EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
/**
+ * The emergency call routing, only valid if {@link #getServiceType} returns
+ * {@link #SERVICE_TYPE_EMERGENCY}
+ *
+ * If valid, the value is any of the following constants:
+ * <ol>
+ * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_UNKNOWN} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_NORMAL} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_EMERGENCY} </li>
+ * </ol>
+ */
+ private @EmergencyCallRouting int mEmergencyCallRouting =
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN;
+
+ /**
* Extras associated with this {@link ImsCallProfile}.
* <p>
* Valid data types include:
@@ -503,10 +519,12 @@
@Override
public String toString() {
- return "{ serviceType=" + mServiceType +
- ", callType=" + mCallType +
- ", restrictCause=" + mRestrictCause +
- ", mediaProfile=" + mMediaProfile.toString() + " }";
+ return "{ serviceType=" + mServiceType
+ + ", callType=" + mCallType
+ + ", restrictCause=" + mRestrictCause
+ + ", mediaProfile=" + mMediaProfile.toString()
+ + ", emergencyServiceCategories=" + mEmergencyCallRouting
+ + ", emergencyCallRouting=" + mEmergencyCallRouting + " }";
}
@Override
@@ -522,6 +540,7 @@
out.writeBundle(filteredExtras);
out.writeParcelable(mMediaProfile, 0);
out.writeInt(mEmergencyServiceCategories);
+ out.writeInt(mEmergencyCallRouting);
}
private void readFromParcel(Parcel in) {
@@ -530,6 +549,7 @@
mCallExtras = in.readBundle();
mMediaProfile = in.readParcelable(ImsStreamMediaProfile.class.getClassLoader());
mEmergencyServiceCategories = in.readInt();
+ mEmergencyCallRouting = in.readInt();
}
public static final Creator<ImsCallProfile> CREATOR = new Creator<ImsCallProfile>() {
@@ -740,6 +760,21 @@
}
/**
+ * Set the emergency service categories and emergency call routing. The set value is valid
+ * only if {@link #getServiceType} returns {@link #SERVICE_TYPE_EMERGENCY}
+ *
+ * Reference: 3gpp 23.167, Section 6 - Functional description;
+ * 3gpp 22.101, Section 10 - Emergency Calls.
+ *
+ * @hide
+ */
+ public void setEmergencyCallInfo(EmergencyNumber num) {
+ setEmergencyServiceCategories(num.getEmergencyServiceCategoryBitmask());
+ setEmergencyCallRouting(num.getEmergencyCallRouting());
+ }
+
+
+ /**
* Set the emergency service categories. The set value is valid only if
* {@link #getServiceType} returns {@link #SERVICE_TYPE_EMERGENCY}
*
@@ -758,12 +793,29 @@
* Reference: 3gpp 23.167, Section 6 - Functional description;
* 3gpp 22.101, Section 10 - Emergency Calls.
*/
+ @VisibleForTesting
public void setEmergencyServiceCategories(
@EmergencyServiceCategories int emergencyServiceCategories) {
mEmergencyServiceCategories = emergencyServiceCategories;
}
/**
+ * Set the emergency call routing, only valid if {@link #getServiceType} returns
+ * {@link #SERVICE_TYPE_EMERGENCY}
+ *
+ * If valid, the value is any of the following constants:
+ * <ol>
+ * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_UNKNOWN} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_NORMAL} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_EMERGENCY} </li>
+ * </ol>
+ */
+ @VisibleForTesting
+ public void setEmergencyCallRouting(@EmergencyCallRouting int emergencyCallRouting) {
+ mEmergencyCallRouting = emergencyCallRouting;
+ }
+
+ /**
* Get the emergency service categories, only valid if {@link #getServiceType} returns
* {@link #SERVICE_TYPE_EMERGENCY}
*
@@ -787,4 +839,19 @@
public @EmergencyServiceCategories int getEmergencyServiceCategories() {
return mEmergencyServiceCategories;
}
+
+ /**
+ * Get the emergency call routing, only valid if {@link #getServiceType} returns
+ * {@link #SERVICE_TYPE_EMERGENCY}
+ *
+ * If valid, the value is any of the following constants:
+ * <ol>
+ * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_UNKNOWN} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_NORMAL} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_EMERGENCY} </li>
+ * </ol>
+ */
+ public @EmergencyCallRouting int getEmergencyCallRouting() {
+ return mEmergencyCallRouting;
+ }
}
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index 122626f..e2350fe 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -714,7 +714,7 @@
* @see #setVoWiFiRoamingSetting(boolean)
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- @WiFiCallingMode int getVoWiFiRoamingModeSetting() {
+ public @WiFiCallingMode int getVoWiFiRoamingModeSetting() {
try {
return getITelephony().getVoWiFiRoamingModeSetting(mSubId);
} catch (RemoteException e) {
diff --git a/tests/net/java/android/net/LinkPropertiesTest.java b/tests/net/java/android/net/LinkPropertiesTest.java
index 9695e9a..f82b380 100644
--- a/tests/net/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/java/android/net/LinkPropertiesTest.java
@@ -53,6 +53,8 @@
private static InetAddress DNS1 = NetworkUtils.numericToInetAddress("75.208.7.1");
private static InetAddress DNS2 = NetworkUtils.numericToInetAddress("69.78.7.1");
private static InetAddress DNS6 = NetworkUtils.numericToInetAddress("2001:4860:4860::8888");
+ private static InetAddress PCSCFV6 = NetworkUtils.numericToInetAddress(
+ "2001:0db8:85a3:0000:0000:8a2e:0370:1");
private static InetAddress GATEWAY1 = NetworkUtils.numericToInetAddress("75.208.8.1");
private static InetAddress GATEWAY2 = NetworkUtils.numericToInetAddress("69.78.8.1");
private static InetAddress GATEWAY61 = NetworkUtils.numericToInetAddress("fe80::6:0000:613");
@@ -86,6 +88,9 @@
assertTrue(source.isIdenticalValidatedPrivateDnses(target));
assertTrue(target.isIdenticalValidatedPrivateDnses(source));
+ assertTrue(source.isIdenticalPcscfs(target));
+ assertTrue(target.isIdenticalPcscfs(source));
+
assertTrue(source.isIdenticalRoutes(target));
assertTrue(target.isIdenticalRoutes(source));
@@ -128,6 +133,8 @@
// set 2 dnses
source.addDnsServer(DNS1);
source.addDnsServer(DNS2);
+ // set 1 pcscf
+ source.addPcscfServer(PCSCFV6);
// set 2 gateways
source.addRoute(new RouteInfo(GATEWAY1));
source.addRoute(new RouteInfo(GATEWAY2));
@@ -141,6 +148,7 @@
target.addLinkAddress(LINKADDRV6);
target.addDnsServer(DNS1);
target.addDnsServer(DNS2);
+ target.addPcscfServer(PCSCFV6);
target.addRoute(new RouteInfo(GATEWAY1));
target.addRoute(new RouteInfo(GATEWAY2));
target.setMtu(MTU);
@@ -154,6 +162,7 @@
target.addLinkAddress(LINKADDRV6);
target.addDnsServer(DNS1);
target.addDnsServer(DNS2);
+ target.addPcscfServer(PCSCFV6);
target.addRoute(new RouteInfo(GATEWAY1));
target.addRoute(new RouteInfo(GATEWAY2));
target.setMtu(MTU);
@@ -167,6 +176,7 @@
target.addLinkAddress(LINKADDRV6);
target.addDnsServer(DNS1);
target.addDnsServer(DNS2);
+ target.addPcscfServer(PCSCFV6);
target.addRoute(new RouteInfo(GATEWAY1));
target.addRoute(new RouteInfo(GATEWAY2));
target.setMtu(MTU);
@@ -179,6 +189,21 @@
// change dnses
target.addDnsServer(NetworkUtils.numericToInetAddress("75.208.7.2"));
target.addDnsServer(DNS2);
+ target.addPcscfServer(PCSCFV6);
+ target.addRoute(new RouteInfo(GATEWAY1));
+ target.addRoute(new RouteInfo(GATEWAY2));
+ target.setMtu(MTU);
+ assertFalse(source.equals(target));
+
+ target.clear();
+ target.setInterfaceName(NAME);
+ target.addLinkAddress(LINKADDRV4);
+ target.addLinkAddress(LINKADDRV6);
+ target.addDnsServer(NetworkUtils.numericToInetAddress("75.208.7.2"));
+ target.addDnsServer(DNS2);
+ // change pcscf
+ target.addPcscfServer(NetworkUtils.numericToInetAddress(
+ "2001::1"));
target.addRoute(new RouteInfo(GATEWAY1));
target.addRoute(new RouteInfo(GATEWAY2));
target.setMtu(MTU);
diff --git a/wifi/java/com/android/server/wifi/AbstractWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java
similarity index 97%
rename from wifi/java/com/android/server/wifi/AbstractWifiService.java
rename to wifi/java/com/android/server/wifi/BaseWifiService.java
index f06346c..4e29dd1 100644
--- a/wifi/java/com/android/server/wifi/AbstractWifiService.java
+++ b/wifi/java/com/android/server/wifi/BaseWifiService.java
@@ -38,14 +38,13 @@
import android.os.Messenger;
import android.os.RemoteException;
import android.os.ResultReceiver;
-import android.os.RemoteException;
import android.os.WorkSource;
import java.util.List;
import java.util.Map;
/**
- * Abstract class implementing IWifiManager with stub methods throwing runtime exceptions.
+ * Empty concrete class implementing IWifiManager with stub methods throwing runtime exceptions.
*
* This class is meant to be extended by real implementations of IWifiManager in order to facilitate
* cross-repo changes to WiFi internal APIs, including the introduction of new APIs, the removal of
@@ -56,10 +55,13 @@
* then given a short grace period to update themselves before the @Deprecated stub is removed for
* good. If the API scheduled for removal has a replacement or an overload (signature change),
* these should be introduced before the stub is removed to allow children to migrate.
+ *
+ * When a new API is added to IWifiManager.aidl, a stub should be added in BaseWifiService as
+ * well otherwise compilation will fail.
*/
-public abstract class AbstractWifiService extends IWifiManager.Stub {
+public class BaseWifiService extends IWifiManager.Stub {
- private static final String TAG = AbstractWifiService.class.getSimpleName();
+ private static final String TAG = BaseWifiService.class.getSimpleName();
@Override
public int getSupportedFeatures() {