diff options
103 files changed, 2185 insertions, 910 deletions
diff --git a/api/current.txt b/api/current.txt index 6880c0ba18e6..e6bacc041985 100644 --- a/api/current.txt +++ b/api/current.txt @@ -4167,6 +4167,7 @@ package android.app { field public static final java.lang.String OPSTR_MOCK_LOCATION = "android:mock_location"; field public static final java.lang.String OPSTR_MONITOR_HIGH_POWER_LOCATION = "android:monitor_location_high_power"; field public static final java.lang.String OPSTR_MONITOR_LOCATION = "android:monitor_location"; + field public static final java.lang.String OPSTR_PICTURE_IN_PICTURE = "android:picture_in_picture"; field public static final java.lang.String OPSTR_PROCESS_OUTGOING_CALLS = "android:process_outgoing_calls"; field public static final java.lang.String OPSTR_READ_CALENDAR = "android:read_calendar"; field public static final java.lang.String OPSTR_READ_CALL_LOG = "android:read_call_log"; @@ -5705,7 +5706,7 @@ package android.app { method public android.app.RemoteAction getUserAction(); method public java.lang.CharSequence getUserMessage(); method public void showAsDialog(android.app.Activity); - method public void showAsNotification(android.content.Context); + method public void showAsNotification(android.content.Context, java.lang.String); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.app.RecoverableSecurityException> CREATOR; } @@ -7939,10 +7940,10 @@ package android.bluetooth.le { public final class AdvertisingSet { method public void enableAdvertising(boolean, int); - method public void periodicAdvertisingEnable(boolean); method public void setAdvertisingData(android.bluetooth.le.AdvertiseData); method public void setAdvertisingParameters(android.bluetooth.le.AdvertisingSetParameters); method public void setPeriodicAdvertisingData(android.bluetooth.le.AdvertiseData); + method public void setPeriodicAdvertisingEnable(boolean); method public void setPeriodicAdvertisingParameters(android.bluetooth.le.PeriodicAdvertisingParameters); method public void setScanResponseData(android.bluetooth.le.AdvertiseData); } @@ -7951,8 +7952,8 @@ package android.bluetooth.le { ctor public AdvertisingSetCallback(); method public void onAdvertisingDataSet(android.bluetooth.le.AdvertisingSet, int); method public void onAdvertisingEnabled(android.bluetooth.le.AdvertisingSet, boolean, int); - method public void onAdvertisingParametersUpdated(android.bluetooth.le.AdvertisingSet, int); - method public void onAdvertisingSetStarted(android.bluetooth.le.AdvertisingSet, int); + method public void onAdvertisingParametersUpdated(android.bluetooth.le.AdvertisingSet, int, int); + method public void onAdvertisingSetStarted(android.bluetooth.le.AdvertisingSet, int, int); method public void onAdvertisingSetStopped(android.bluetooth.le.AdvertisingSet); method public void onPeriodicAdvertisingDataSet(android.bluetooth.le.AdvertisingSet, int); method public void onPeriodicAdvertisingEnable(android.bluetooth.le.AdvertisingSet, boolean, int); @@ -39945,7 +39946,6 @@ package android.telephony { method public boolean sendDialerCode(java.lang.String); method public java.lang.String sendEnvelopeWithStatus(java.lang.String); method public void sendUssdRequest(java.lang.String, android.telephony.TelephonyManager.OnReceiveUssdResponseCallback, android.os.Handler); - method public void sendUssdRequest(java.lang.String, int, android.telephony.TelephonyManager.OnReceiveUssdResponseCallback, android.os.Handler); method public void setDataEnabled(boolean); method public boolean setLine1NumberForDisplay(java.lang.String, java.lang.String); method public boolean setOperatorBrandOverride(java.lang.String); @@ -45093,7 +45093,7 @@ package android.view { method public android.view.ViewPropertyAnimator animate(); method public void announceForAccessibility(java.lang.CharSequence); method public boolean autofill(android.view.autofill.AutofillValue); - method public boolean autofill(int, android.view.autofill.AutofillValue); + method public boolean autofill(android.util.SparseArray<android.view.autofill.AutofillValue>); method protected boolean awakenScrollBars(); method protected boolean awakenScrollBars(int); method protected boolean awakenScrollBars(int, boolean); diff --git a/api/removed.txt b/api/removed.txt index 148f3f1e3281..04c9c35428b3 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -4,6 +4,10 @@ package android.app { method public deprecated void setLatestEventInfo(android.content.Context, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent); } + public final class RecoverableSecurityException extends java.lang.SecurityException implements android.os.Parcelable { + method public deprecated void showAsNotification(android.content.Context); + } + } package android.app.admin { diff --git a/api/system-current.txt b/api/system-current.txt index 3dac882e2d04..173aaf3db262 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -4310,6 +4310,7 @@ package android.app { field public static final java.lang.String OPSTR_MOCK_LOCATION = "android:mock_location"; field public static final java.lang.String OPSTR_MONITOR_HIGH_POWER_LOCATION = "android:monitor_location_high_power"; field public static final java.lang.String OPSTR_MONITOR_LOCATION = "android:monitor_location"; + field public static final java.lang.String OPSTR_PICTURE_IN_PICTURE = "android:picture_in_picture"; field public static final java.lang.String OPSTR_PROCESS_OUTGOING_CALLS = "android:process_outgoing_calls"; field public static final java.lang.String OPSTR_READ_CALENDAR = "android:read_calendar"; field public static final java.lang.String OPSTR_READ_CALL_LOG = "android:read_call_log"; @@ -5002,10 +5003,8 @@ package android.app { ctor public InstantAppResolverService(); method public final void attachBaseContext(android.content.Context); method public final android.os.IBinder onBind(android.content.Intent); - method public void onGetInstantAppIntentFilter(int[], android.app.InstantAppResolverService.InstantAppResolutionCallback); - method public void onGetInstantAppResolveInfo(int[], android.app.InstantAppResolverService.InstantAppResolutionCallback); - field public static final java.lang.String EXTRA_RESOLVE_INFO = "android.app.extra.RESOLVE_INFO"; - field public static final java.lang.String EXTRA_SEQUENCE = "android.app.extra.SEQUENCE"; + method public void onGetInstantAppIntentFilter(int[], java.lang.String, android.app.InstantAppResolverService.InstantAppResolutionCallback); + method public void onGetInstantAppResolveInfo(int[], java.lang.String, android.app.InstantAppResolverService.InstantAppResolutionCallback); } public static final class InstantAppResolverService.InstantAppResolutionCallback { @@ -5908,7 +5907,7 @@ package android.app { method public android.app.RemoteAction getUserAction(); method public java.lang.CharSequence getUserMessage(); method public void showAsDialog(android.app.Activity); - method public void showAsNotification(android.content.Context); + method public void showAsNotification(android.content.Context, java.lang.String); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.app.RecoverableSecurityException> CREATOR; } @@ -8414,10 +8413,10 @@ package android.bluetooth.le { public final class AdvertisingSet { method public void enableAdvertising(boolean, int); - method public void periodicAdvertisingEnable(boolean); method public void setAdvertisingData(android.bluetooth.le.AdvertiseData); method public void setAdvertisingParameters(android.bluetooth.le.AdvertisingSetParameters); method public void setPeriodicAdvertisingData(android.bluetooth.le.AdvertiseData); + method public void setPeriodicAdvertisingEnable(boolean); method public void setPeriodicAdvertisingParameters(android.bluetooth.le.PeriodicAdvertisingParameters); method public void setScanResponseData(android.bluetooth.le.AdvertiseData); } @@ -8426,8 +8425,8 @@ package android.bluetooth.le { ctor public AdvertisingSetCallback(); method public void onAdvertisingDataSet(android.bluetooth.le.AdvertisingSet, int); method public void onAdvertisingEnabled(android.bluetooth.le.AdvertisingSet, boolean, int); - method public void onAdvertisingParametersUpdated(android.bluetooth.le.AdvertisingSet, int); - method public void onAdvertisingSetStarted(android.bluetooth.le.AdvertisingSet, int); + method public void onAdvertisingParametersUpdated(android.bluetooth.le.AdvertisingSet, int, int); + method public void onAdvertisingSetStarted(android.bluetooth.le.AdvertisingSet, int, int); method public void onAdvertisingSetStopped(android.bluetooth.le.AdvertisingSet); method public void onPeriodicAdvertisingDataSet(android.bluetooth.le.AdvertisingSet, int); method public void onPeriodicAdvertisingEnable(android.bluetooth.le.AdvertisingSet, boolean, int); @@ -43362,7 +43361,6 @@ package android.telephony { method public boolean sendDialerCode(java.lang.String); method public java.lang.String sendEnvelopeWithStatus(java.lang.String); method public void sendUssdRequest(java.lang.String, android.telephony.TelephonyManager.OnReceiveUssdResponseCallback, android.os.Handler); - method public void sendUssdRequest(java.lang.String, int, android.telephony.TelephonyManager.OnReceiveUssdResponseCallback, android.os.Handler); method public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>); method public void setDataEnabled(boolean); method public void setDataEnabled(int, boolean); @@ -48558,7 +48556,7 @@ package android.view { method public android.view.ViewPropertyAnimator animate(); method public void announceForAccessibility(java.lang.CharSequence); method public boolean autofill(android.view.autofill.AutofillValue); - method public boolean autofill(int, android.view.autofill.AutofillValue); + method public boolean autofill(android.util.SparseArray<android.view.autofill.AutofillValue>); method protected boolean awakenScrollBars(); method protected boolean awakenScrollBars(int); method protected boolean awakenScrollBars(int, boolean); diff --git a/api/system-removed.txt b/api/system-removed.txt index bd535d2be513..640dc81877a1 100644 --- a/api/system-removed.txt +++ b/api/system-removed.txt @@ -4,6 +4,10 @@ package android.app { method public deprecated void setLatestEventInfo(android.content.Context, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent); } + public final class RecoverableSecurityException extends java.lang.SecurityException implements android.os.Parcelable { + method public deprecated void showAsNotification(android.content.Context); + } + } package android.app.admin { diff --git a/api/test-current.txt b/api/test-current.txt index a891fde573b2..99d960ba3c3f 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -4177,6 +4177,7 @@ package android.app { field public static final java.lang.String OPSTR_MOCK_LOCATION = "android:mock_location"; field public static final java.lang.String OPSTR_MONITOR_HIGH_POWER_LOCATION = "android:monitor_location_high_power"; field public static final java.lang.String OPSTR_MONITOR_LOCATION = "android:monitor_location"; + field public static final java.lang.String OPSTR_PICTURE_IN_PICTURE = "android:picture_in_picture"; field public static final java.lang.String OPSTR_PROCESS_OUTGOING_CALLS = "android:process_outgoing_calls"; field public static final java.lang.String OPSTR_READ_CALENDAR = "android:read_calendar"; field public static final java.lang.String OPSTR_READ_CALL_LOG = "android:read_call_log"; @@ -5716,7 +5717,7 @@ package android.app { method public android.app.RemoteAction getUserAction(); method public java.lang.CharSequence getUserMessage(); method public void showAsDialog(android.app.Activity); - method public void showAsNotification(android.content.Context); + method public void showAsNotification(android.content.Context, java.lang.String); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.app.RecoverableSecurityException> CREATOR; } @@ -7966,10 +7967,10 @@ package android.bluetooth.le { public final class AdvertisingSet { method public void enableAdvertising(boolean, int); - method public void periodicAdvertisingEnable(boolean); method public void setAdvertisingData(android.bluetooth.le.AdvertiseData); method public void setAdvertisingParameters(android.bluetooth.le.AdvertisingSetParameters); method public void setPeriodicAdvertisingData(android.bluetooth.le.AdvertiseData); + method public void setPeriodicAdvertisingEnable(boolean); method public void setPeriodicAdvertisingParameters(android.bluetooth.le.PeriodicAdvertisingParameters); method public void setScanResponseData(android.bluetooth.le.AdvertiseData); } @@ -7978,8 +7979,8 @@ package android.bluetooth.le { ctor public AdvertisingSetCallback(); method public void onAdvertisingDataSet(android.bluetooth.le.AdvertisingSet, int); method public void onAdvertisingEnabled(android.bluetooth.le.AdvertisingSet, boolean, int); - method public void onAdvertisingParametersUpdated(android.bluetooth.le.AdvertisingSet, int); - method public void onAdvertisingSetStarted(android.bluetooth.le.AdvertisingSet, int); + method public void onAdvertisingParametersUpdated(android.bluetooth.le.AdvertisingSet, int, int); + method public void onAdvertisingSetStarted(android.bluetooth.le.AdvertisingSet, int, int); method public void onAdvertisingSetStopped(android.bluetooth.le.AdvertisingSet); method public void onPeriodicAdvertisingDataSet(android.bluetooth.le.AdvertisingSet, int); method public void onPeriodicAdvertisingEnable(android.bluetooth.le.AdvertisingSet, boolean, int); @@ -40136,7 +40137,6 @@ package android.telephony { method public boolean sendDialerCode(java.lang.String); method public java.lang.String sendEnvelopeWithStatus(java.lang.String); method public void sendUssdRequest(java.lang.String, android.telephony.TelephonyManager.OnReceiveUssdResponseCallback, android.os.Handler); - method public void sendUssdRequest(java.lang.String, int, android.telephony.TelephonyManager.OnReceiveUssdResponseCallback, android.os.Handler); method public void setDataEnabled(boolean); method public boolean setLine1NumberForDisplay(java.lang.String, java.lang.String); method public boolean setOperatorBrandOverride(java.lang.String); @@ -45455,7 +45455,7 @@ package android.view { method public android.view.ViewPropertyAnimator animate(); method public void announceForAccessibility(java.lang.CharSequence); method public boolean autofill(android.view.autofill.AutofillValue); - method public boolean autofill(int, android.view.autofill.AutofillValue); + method public boolean autofill(android.util.SparseArray<android.view.autofill.AutofillValue>); method protected boolean awakenScrollBars(); method protected boolean awakenScrollBars(int); method protected boolean awakenScrollBars(int, boolean); diff --git a/api/test-removed.txt b/api/test-removed.txt index 148f3f1e3281..04c9c35428b3 100644 --- a/api/test-removed.txt +++ b/api/test-removed.txt @@ -4,6 +4,10 @@ package android.app { method public deprecated void setLatestEventInfo(android.content.Context, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent); } + public final class RecoverableSecurityException extends java.lang.SecurityException implements android.os.Parcelable { + method public deprecated void showAsNotification(android.content.Context); + } + } package android.app.admin { diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 37a11ec6416b..07540f3bfcea 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -7193,6 +7193,7 @@ public class Activity extends ContextThemeWrapper final View root = getWindow().getDecorView(); final int itemCount = ids.size(); int numApplied = 0; + ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null; for (int i = 0; i < itemCount; i++) { final AutofillId id = ids.get(i); @@ -7203,19 +7204,37 @@ public class Activity extends ContextThemeWrapper Log.w(TAG, "autofill(): no View with id " + viewId); continue; } - final boolean wasApplied; if (id.isVirtual()) { - wasApplied = view.autofill(id.getVirtualChildId(), value); + final int parentId = id.getViewId(); + if (virtualValues == null) { + // Most likely there will be just one view with virtual children. + virtualValues = new ArrayMap<>(1); + } + SparseArray<AutofillValue> valuesByParent = virtualValues.get(view); + if (valuesByParent == null) { + // We don't know the size yet, but usually it will be just a few fields... + valuesByParent = new SparseArray<>(5); + virtualValues.put(view, valuesByParent); + } + valuesByParent.put(id.getVirtualChildId(), value); } else { - wasApplied = view.autofill(value); + if (view.autofill(value)) { + numApplied++; + } } + } - if (wasApplied) { - numApplied++; + if (virtualValues != null) { + for (int i = 0; i < virtualValues.size(); i++) { + final View parent = virtualValues.keyAt(i); + final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i); + if (parent.autofill(childrenValues)) { + numApplied += childrenValues.size(); + } } } - LogMaker log = new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_DATASET_APPLIED); + final LogMaker log = new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_DATASET_APPLIED); log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount); log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, numApplied); mMetricsLogger.write(log); diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 043e0ab35e3e..8a0af9a54056 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -4060,6 +4060,10 @@ public class ActivityManager { * thread can be a VR thread in a process at a time, and that thread may be subject to * restrictions on the amount of time it can run. * + * If persistent VR mode is set, whatever thread has been granted aggressive scheduling via this + * method will return to normal operation, and calling this method will do nothing while + * persistent VR mode is enabled. + * * To reset the VR thread for an application, a tid of 0 can be passed. * * @see android.os.Process#myTid() @@ -4074,6 +4078,31 @@ public class ActivityManager { } /** + * Enable more aggressive scheduling for latency-sensitive low-runtime VR threads that persist + * beyond a single process. It requires holding the + * {@link android.Manifest.permission#RESTRICTED_VR_ACCESS} permission. Only one thread can be a + * persistent VR thread at a time, and that thread may be subject to restrictions on the amount + * of time it can run. Calling this method will disable aggressive scheduling for non-persistent + * VR threads set via {@link #setVrThread}. If persistent VR mode is disabled then the + * persistent VR thread loses its new scheduling priority; this method must be called again to + * set the persistent thread. + * + * To reset the persistent VR thread, a tid of 0 can be passed. + * + * @see android.os.Process#myTid() + * @param tid tid of the VR thread + * @hide + */ + @RequiresPermission(Manifest.permission.RESTRICTED_VR_ACCESS) + public static void setPersistentVrThread(int tid) { + try { + getService().setPersistentVrThread(tid); + } catch (RemoteException e) { + // pass + } + } + + /** * The AppTask allows you to manage your own application's tasks. * See {@link android.app.ActivityManager#getAppTasks()} */ diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 09e7595242af..cbd7b9d4aa9c 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -245,8 +245,8 @@ public class AppOpsManager { public static final int OP_READ_PHONE_NUMBER = 65; /** @hide Request package installs through package installer */ public static final int OP_REQUEST_INSTALL_PACKAGES = 66; - /** @hide Enter picture-in-picture when hidden. */ - public static final int OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE = 67; + /** @hide Enter picture-in-picture. */ + public static final int OP_PICTURE_IN_PICTURE = 67; /** @hide Instant app start foreground service. */ public static final int OP_INSTANT_APP_START_FOREGROUND = 68; /** @hide Answer incoming phone calls */ @@ -355,6 +355,9 @@ public class AppOpsManager { = "android:get_accounts"; public static final String OPSTR_READ_PHONE_NUMBER = "android:read_phone_number"; + /** Access to picture-in-picture. */ + public static final String OPSTR_PICTURE_IN_PICTURE + = "android:picture_in_picture"; /** @hide */ public static final String OPSTR_INSTANT_APP_START_FOREGROUND = "android:instant_app_start_foreground"; @@ -486,7 +489,7 @@ public class AppOpsManager { OP_AUDIO_ACCESSIBILITY_VOLUME, OP_READ_PHONE_NUMBER, OP_REQUEST_INSTALL_PACKAGES, - OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE, + OP_PICTURE_IN_PICTURE, OP_INSTANT_APP_START_FOREGROUND, OP_ANSWER_PHONE_CALLS }; @@ -563,7 +566,7 @@ public class AppOpsManager { null, // OP_AUDIO_ACCESSIBILITY_VOLUME OPSTR_READ_PHONE_NUMBER, null, // OP_REQUEST_INSTALL_PACKAGES - null, + OPSTR_PICTURE_IN_PICTURE, OPSTR_INSTANT_APP_START_FOREGROUND, OPSTR_ANSWER_PHONE_CALLS, }; @@ -640,7 +643,7 @@ public class AppOpsManager { "AUDIO_ACCESSIBILITY_VOLUME", "READ_PHONE_NUMBER", "REQUEST_INSTALL_PACKAGES", - "OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE", + "PICTURE_IN_PICTURE", "INSTANT_APP_START_FOREGROUND", "ANSWER_PHONE_CALLS", }; @@ -948,7 +951,7 @@ public class AppOpsManager { AppOpsManager.MODE_ALLOWED, // OP_AUDIO_ACCESSIBILITY_VOLUME AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_DEFAULT, // OP_REQUEST_INSTALL_PACKAGES - AppOpsManager.MODE_ALLOWED, // OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE + AppOpsManager.MODE_ALLOWED, // OP_PICTURE_IN_PICTURE AppOpsManager.MODE_DEFAULT, // OP_INSTANT_APP_START_FOREGROUND AppOpsManager.MODE_ALLOWED, // ANSWER_PHONE_CALLS }; @@ -1028,7 +1031,7 @@ public class AppOpsManager { false, // OP_AUDIO_ACCESSIBILITY_VOLUME false, false, // OP_REQUEST_INSTALL_PACKAGES - false, // OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE + false, // OP_PICTURE_IN_PICTURE false, false, // ANSWER_PHONE_CALLS }; diff --git a/core/java/android/app/EphemeralResolverService.java b/core/java/android/app/EphemeralResolverService.java index 445d3bd237b8..bbd8ab3bcd8a 100644 --- a/core/java/android/app/EphemeralResolverService.java +++ b/core/java/android/app/EphemeralResolverService.java @@ -23,6 +23,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.EphemeralResolveInfo; import android.content.pm.InstantAppResolveInfo; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -30,8 +31,10 @@ import android.os.IRemoteCallback; import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.util.Log; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -42,6 +45,9 @@ import java.util.List; @Deprecated @SystemApi public abstract class EphemeralResolverService extends InstantAppResolverService { + private static final boolean DEBUG_EPHEMERAL = Build.IS_DEBUGGABLE; + private static final String TAG = "PackageManager"; + /** * Called to retrieve resolve info for ephemeral applications. * @@ -79,7 +85,12 @@ public abstract class EphemeralResolverService extends InstantAppResolverService } @Override - void _onGetInstantAppResolveInfo(int[] digestPrefix, InstantAppResolutionCallback callback) { + void _onGetInstantAppResolveInfo(int[] digestPrefix, String token, + InstantAppResolutionCallback callback) { + if (DEBUG_EPHEMERAL) { + Log.d(TAG, "Legacy resolver; getInstantAppResolveInfo;" + + " prefix: " + Arrays.toString(digestPrefix)); + } final List<EphemeralResolveInfo> response = onGetEphemeralResolveInfo(digestPrefix); final int responseSize = response == null ? 0 : response.size(); final List<InstantAppResolveInfo> resultList = new ArrayList<>(responseSize); @@ -90,8 +101,12 @@ public abstract class EphemeralResolverService extends InstantAppResolverService } @Override - void _onGetInstantAppIntentFilter(int[] digestPrefix, String hostName, - InstantAppResolutionCallback callback) { + void _onGetInstantAppIntentFilter(int[] digestPrefix, String token, + String hostName, InstantAppResolutionCallback callback) { + if (DEBUG_EPHEMERAL) { + Log.d(TAG, "Legacy resolver; getInstantAppIntentFilter;" + + " prefix: " + Arrays.toString(digestPrefix)); + } final EphemeralResolveInfo response = onGetEphemeralIntentFilter(hostName); final List<InstantAppResolveInfo> resultList = new ArrayList<>(1); resultList.add(response.getInstantAppResolveInfo()); diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 77edaeacfd2a..f5e0c38c9f2f 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -605,6 +605,7 @@ interface IActivityManager { ActivityManager.TaskSnapshot getTaskSnapshot(int taskId); void scheduleApplicationInfoChanged(in List<String> packageNames, int userId); + void setPersistentVrThread(int tid); // WARNING: when these transactions are updated, check if they are any callers on the native // side. If so, make sure they are using the correct transaction ids and arguments. diff --git a/core/java/android/app/IInstantAppResolver.aidl b/core/java/android/app/IInstantAppResolver.aidl index 04e321f89c88..805d8c057d27 100644 --- a/core/java/android/app/IInstantAppResolver.aidl +++ b/core/java/android/app/IInstantAppResolver.aidl @@ -21,8 +21,8 @@ import android.os.IRemoteCallback; /** @hide */ oneway interface IInstantAppResolver { void getInstantAppResolveInfoList(in int[] digestPrefix, - int sequence, IRemoteCallback callback); + String token, int sequence, IRemoteCallback callback); void getInstantAppIntentFilterList(in int[] digestPrefix, - int sequence, String hostName, IRemoteCallback callback); + String token, String hostName, IRemoteCallback callback); } diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl index 5768d1a0393f..47817a72e962 100644 --- a/core/java/android/app/ITaskStackListener.aidl +++ b/core/java/android/app/ITaskStackListener.aidl @@ -25,7 +25,10 @@ oneway interface ITaskStackListener { void onTaskStackChanged(); /** Called whenever an Activity is moved to the pinned stack from another stack. */ - void onActivityPinned(); + void onActivityPinned(String packageName); + + /** Called whenever an Activity is moved from the pinned stack to another stack. */ + void onActivityUnpinned(); /** * Called whenever IActivityManager.startActivity is called on an activity that is already diff --git a/core/java/android/app/InstantAppResolverService.java b/core/java/android/app/InstantAppResolverService.java index 1ce30b258a54..2bdfa99fee47 100644 --- a/core/java/android/app/InstantAppResolverService.java +++ b/core/java/android/app/InstantAppResolverService.java @@ -29,6 +29,8 @@ import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import com.android.internal.os.SomeArgs; + import java.util.List; /** @@ -37,10 +39,10 @@ import java.util.List; */ @SystemApi public abstract class InstantAppResolverService extends Service { + /** @hide */ public static final String EXTRA_RESOLVE_INFO = "android.app.extra.RESOLVE_INFO"; + /** @hide */ public static final String EXTRA_SEQUENCE = "android.app.extra.SEQUENCE"; - static final String EXTRA_PREFIX = "android.app.PREFIX"; - static final String EXTRA_HOSTNAME = "android.app.HOSTNAME"; Handler mHandler; /** @@ -49,7 +51,7 @@ public abstract class InstantAppResolverService extends Service { * @param digestPrefix The hash prefix of the instant app's domain. */ public void onGetInstantAppResolveInfo( - int digestPrefix[], InstantAppResolutionCallback callback) { + int digestPrefix[], String token, InstantAppResolutionCallback callback) { throw new IllegalStateException("Must define"); } @@ -59,7 +61,7 @@ public abstract class InstantAppResolverService extends Service { * @param digestPrefix The hash prefix of the instant app's domain. */ public void onGetInstantAppIntentFilter( - int digestPrefix[], InstantAppResolutionCallback callback) { + int digestPrefix[], String token, InstantAppResolutionCallback callback) { throw new IllegalStateException("Must define"); } @@ -81,25 +83,26 @@ public abstract class InstantAppResolverService extends Service { return new IInstantAppResolver.Stub() { @Override public void getInstantAppResolveInfoList( - int digestPrefix[], int sequence, IRemoteCallback callback) { - final Message msg = mHandler.obtainMessage( - ServiceHandler.MSG_GET_INSTANT_APP_RESOLVE_INFO, sequence, 0, callback); - final Bundle data = new Bundle(); - data.putIntArray(EXTRA_PREFIX, digestPrefix); - msg.setData(data); - msg.sendToTarget(); + int digestPrefix[], String token, int sequence, IRemoteCallback callback) { + final SomeArgs args = SomeArgs.obtain(); + args.arg1 = callback; + args.arg2 = digestPrefix; + args.arg3 = token; + mHandler.obtainMessage( + ServiceHandler.MSG_GET_INSTANT_APP_RESOLVE_INFO, sequence, 0, args) + .sendToTarget(); } @Override public void getInstantAppIntentFilterList( - int digestPrefix[], int sequence, String hostName, IRemoteCallback callback) { - final Message msg = mHandler.obtainMessage( - ServiceHandler.MSG_GET_INSTANT_APP_INTENT_FILTER, sequence, 0, callback); - final Bundle data = new Bundle(); - data.putString(EXTRA_HOSTNAME, hostName); - data.putIntArray(EXTRA_PREFIX, digestPrefix); - msg.setData(data); - msg.sendToTarget(); + int digestPrefix[], String token, String hostName, IRemoteCallback callback) { + final SomeArgs args = SomeArgs.obtain(); + args.arg1 = callback; + args.arg2 = digestPrefix; + args.arg3 = token; + args.arg4 = hostName; + mHandler.obtainMessage( + ServiceHandler.MSG_GET_INSTANT_APP_INTENT_FILTER, callback).sendToTarget(); } }; } @@ -117,8 +120,8 @@ public abstract class InstantAppResolverService extends Service { public void onInstantAppResolveInfo(List<InstantAppResolveInfo> resolveInfo) { final Bundle data = new Bundle(); - data.putInt(EXTRA_SEQUENCE, mSequence); data.putParcelableList(EXTRA_RESOLVE_INFO, resolveInfo); + data.putInt(EXTRA_SEQUENCE, mSequence); try { mCallback.sendResult(data); } catch (RemoteException e) { @@ -127,13 +130,14 @@ public abstract class InstantAppResolverService extends Service { } @Deprecated - void _onGetInstantAppResolveInfo(int[] digestPrefix, InstantAppResolutionCallback callback) { - onGetInstantAppResolveInfo(digestPrefix, callback); + void _onGetInstantAppResolveInfo(int[] digestPrefix, String token, + InstantAppResolutionCallback callback) { + onGetInstantAppResolveInfo(digestPrefix, token, callback); } @Deprecated - void _onGetInstantAppIntentFilter(int digestPrefix[], String hostName, + void _onGetInstantAppIntentFilter(int digestPrefix[], String token, String hostName, InstantAppResolutionCallback callback) { - onGetInstantAppIntentFilter(digestPrefix, callback); + onGetInstantAppIntentFilter(digestPrefix, token, callback); } private final class ServiceHandler extends Handler { @@ -150,21 +154,25 @@ public abstract class InstantAppResolverService extends Service { final int action = message.what; switch (action) { case MSG_GET_INSTANT_APP_RESOLVE_INFO: { - final IRemoteCallback callback = (IRemoteCallback) message.obj; + final SomeArgs args = (SomeArgs) message.obj; + final IRemoteCallback callback = (IRemoteCallback) args.arg1; + final int[] digestPrefix = (int[]) args.arg2; + final String token = (String) args.arg3; final int sequence = message.arg1; - final int[] digestPrefix = message.getData().getIntArray(EXTRA_PREFIX); _onGetInstantAppResolveInfo( - digestPrefix, new InstantAppResolutionCallback(sequence, callback)); + digestPrefix, token, + new InstantAppResolutionCallback(sequence, callback)); } break; case MSG_GET_INSTANT_APP_INTENT_FILTER: { - final IRemoteCallback callback = (IRemoteCallback) message.obj; - final int sequence = message.arg1; - final int[] digestPrefix = message.getData().getIntArray(EXTRA_PREFIX); - final String hostName = message.getData().getString(EXTRA_HOSTNAME); + final SomeArgs args = (SomeArgs) message.obj; + final IRemoteCallback callback = (IRemoteCallback) args.arg1; + final int[] digestPrefix = (int[]) args.arg2; + final String token = (String) args.arg3; + final String hostName = (String) args.arg4; _onGetInstantAppIntentFilter( - digestPrefix, hostName, - new InstantAppResolutionCallback(sequence, callback)); + digestPrefix, token, hostName, + new InstantAppResolutionCallback(-1 /*sequence*/, callback)); } break; default: { diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index aee9d38600fb..8d769305e9e3 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1099,7 +1099,7 @@ public class Notification implements Parcelable * represent this notification. */ public static final int BADGE_ICON_LARGE = 2; - private int mBadgeIcon = BADGE_ICON_LARGE; + private int mBadgeIcon = BADGE_ICON_NONE; /** * Structure to encapsulate a named action that can be shown as part of this notification. diff --git a/core/java/android/app/RecoverableSecurityException.java b/core/java/android/app/RecoverableSecurityException.java index 540d1cda1edd..8612f186ade4 100644 --- a/core/java/android/app/RecoverableSecurityException.java +++ b/core/java/android/app/RecoverableSecurityException.java @@ -16,8 +16,9 @@ package android.app; +import android.content.ContentProvider; +import android.content.ContentResolver; import android.content.Context; -import android.content.res.Resources; import android.graphics.drawable.Icon; import android.os.Bundle; import android.os.Parcel; @@ -31,7 +32,15 @@ import com.android.internal.util.Preconditions; * <p> * This exception is only appropriate where there is a concrete action the user * can take to recover and make forward progress, such as confirming or entering - * authentication credentials. + * authentication credentials, or granting access. + * <p> + * If the receiving app is actively involved with the user, it should present + * the contained recovery details to help the user make forward progress. The + * {@link #showAsDialog(Activity)} and + * {@link #showAsNotification(Context, String)} methods are provided as a + * convenience, but receiving apps are encouraged to use + * {@link #getUserMessage()} and {@link #getUserAction()} to integrate in a more + * natural way if relevant. * <p class="note"> * Note: legacy code that receives this exception may treat it as a general * {@link SecurityException}, and thus there is no guarantee that the messages @@ -66,7 +75,10 @@ public final class RecoverableSecurityException extends SecurityException implem * {@link Activity#setResult(int)} before finishing to * communicate the final status of the recovery. For example, * apps that observe {@link Activity#RESULT_OK} may choose to - * immediately retry their operation. + * immediately retry their operation. If this exception was + * thrown from a {@link ContentProvider}, you should also send + * any relevant {@link ContentResolver#notifyChange} events to + * trigger reloading of data. */ public RecoverableSecurityException(Throwable cause, CharSequence userMessage, RemoteAction userAction) { @@ -101,6 +113,20 @@ public final class RecoverableSecurityException extends SecurityException implem return mUserAction; } + /** @removed */ + @Deprecated + public void showAsNotification(Context context) { + final NotificationManager nm = context.getSystemService(NotificationManager.class); + + // Create a channel per-sender, since we don't want one poorly behaved + // remote app to cause all of our notifications to be blocked + final String channelId = TAG + "_" + mUserAction.getActionIntent().getCreatorUid(); + nm.createNotificationChannel(new NotificationChannel(channelId, TAG, + NotificationManager.IMPORTANCE_DEFAULT)); + + showAsNotification(context, channelId); + } + /** * Convenience method that will show a very simple notification populated * with the details from this exception. @@ -114,23 +140,20 @@ public final class RecoverableSecurityException extends SecurityException implem * <p> * This method will only display the most recent exception from any single * remote UID; notifications from older exceptions will always be replaced. + * + * @param channelId the {@link NotificationChannel} to use, which must have + * been already created using + * {@link NotificationManager#createNotificationChannel}. */ - public void showAsNotification(Context context) { + public void showAsNotification(Context context, String channelId) { final NotificationManager nm = context.getSystemService(NotificationManager.class); - - // Create a channel per-sender, since we don't want one poorly behaved - // remote app to cause all of our notifications to be blocked - final String tag = TAG + "_" + mUserAction.getActionIntent().getCreatorUid(); - nm.createNotificationChannel(new NotificationChannel(tag, TAG, - NotificationManager.IMPORTANCE_DEFAULT)); - - final Notification.Builder builder = new Notification.Builder(context, tag) + final Notification.Builder builder = new Notification.Builder(context, channelId) .setSmallIcon(com.android.internal.R.drawable.ic_print_error) .setContentTitle(mUserAction.getTitle()) .setContentText(mUserMessage) .setContentIntent(mUserAction.getActionIntent()) .setCategory(Notification.CATEGORY_ERROR); - nm.notify(tag, 0, builder.build()); + nm.notify(TAG, mUserAction.getActionIntent().getCreatorUid(), builder.build()); } /** @@ -164,7 +187,13 @@ public final class RecoverableSecurityException extends SecurityException implem ft.commitAllowingStateLoss(); } - /** {@hide} */ + /** + * Implementation detail for + * {@link RecoverableSecurityException#showAsDialog(Activity)}; needs to + * remain static to be recreated across orientation changes. + * + * @hide + */ public static class LocalDialog extends DialogFragment { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java index a07e11e2b8af..57fc874517b7 100644 --- a/core/java/android/app/TaskStackListener.java +++ b/core/java/android/app/TaskStackListener.java @@ -31,7 +31,11 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { } @Override - public void onActivityPinned() throws RemoteException { + public void onActivityPinned(String packageName) throws RemoteException { + } + + @Override + public void onActivityUnpinned() throws RemoteException { } @Override diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl index c281c7f7b0f8..652a1c6098f2 100644 --- a/core/java/android/bluetooth/IBluetoothGatt.aidl +++ b/core/java/android/bluetooth/IBluetoothGatt.aidl @@ -55,13 +55,13 @@ interface IBluetoothGatt { in AdvertiseData periodicData, in int timeout, in IAdvertisingSetCallback callback); void stopAdvertisingSet(in IAdvertisingSetCallback callback); - void enableAdverisingSet(in int advertiserId, in boolean enable, in int timeout); + void enableAdvertisingSet(in int advertiserId, in boolean enable, in int timeout); void setAdvertisingData(in int advertiserId, in AdvertiseData data); void setScanResponseData(in int advertiserId, in AdvertiseData data); void setAdvertisingParameters(in int advertiserId, in AdvertisingSetParameters parameters); void setPeriodicAdvertisingParameters(in int advertiserId, in PeriodicAdvertisingParameters parameters); void setPeriodicAdvertisingData(in int advertiserId, in AdvertiseData data); - void periodicAdvertisingEnable(in int advertiserId, in boolean enable); + void setPeriodicAdvertisingEnable(in int advertiserId, in boolean enable); void registerSync(in ScanResult scanResult, in int skip, in int timeout, in IPeriodicAdvertisingCallback callback); void unregisterSync(in IPeriodicAdvertisingCallback callback); diff --git a/core/java/android/bluetooth/le/AdvertisingSet.java b/core/java/android/bluetooth/le/AdvertisingSet.java index 5524a2bdae18..7355b0d4c763 100644 --- a/core/java/android/bluetooth/le/AdvertisingSet.java +++ b/core/java/android/bluetooth/le/AdvertisingSet.java @@ -65,7 +65,7 @@ public final class AdvertisingSet { */ public void enableAdvertising(boolean enable, int timeout) { try { - gatt.enableAdverisingSet(this.advertiserId, enable, timeout); + gatt.enableAdvertisingSet(this.advertiserId, enable, timeout); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } @@ -143,9 +143,9 @@ public final class AdvertisingSet { * Used to enable/disable periodic advertising. This method returns immediately, the operation * status is delivered through {@code callback.onPeriodicAdvertisingEnable()}. */ - public void periodicAdvertisingEnable(boolean enable) { + public void setPeriodicAdvertisingEnable(boolean enable) { try { - gatt.periodicAdvertisingEnable(this.advertiserId, enable); + gatt.setPeriodicAdvertisingEnable(this.advertiserId, enable); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } diff --git a/core/java/android/bluetooth/le/AdvertisingSetCallback.java b/core/java/android/bluetooth/le/AdvertisingSetCallback.java index ceed8d9e3c60..8d2b82ab350c 100644 --- a/core/java/android/bluetooth/le/AdvertisingSetCallback.java +++ b/core/java/android/bluetooth/le/AdvertisingSetCallback.java @@ -62,9 +62,10 @@ public abstract class AdvertisingSetCallback { * null, and status will be set to proper error code. * * @param advertisingSet The advertising set that was started or null if error. + * @param txPower tx power that will be used for this set. * @param status Status of the operation. */ - public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int status) {} + public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int txPower, int status) {} /** * Callback triggered in response to {@link BluetoothLeAdvertiser#stopAdvertisingSet} @@ -106,10 +107,11 @@ public abstract class AdvertisingSetCallback { * indicating result of the operation. * * @param advertisingSet The advertising set. + * @param txPower tx power that will be used for this set. * @param status Status of the operation. */ public void onAdvertisingParametersUpdated(AdvertisingSet advertisingSet, - int status) {} + int txPower, int status) {} /** * Callback triggered in response to {@link AdvertisingSet#setPeriodicAdvertisingParameters} @@ -133,7 +135,7 @@ public abstract class AdvertisingSetCallback { int status) {} /** - * Callback triggered in response to {@link AdvertisingSet#periodicAdvertisingEnable} + * Callback triggered in response to {@link AdvertisingSet#setPeriodicAdvertisingEnable} * indicating result of the operation. * * @param advertisingSet The advertising set. diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 67fd1c86aa3b..4457bdd7762c 100644 --- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -400,12 +400,12 @@ public final class BluetoothLeAdvertiser { IAdvertisingSetCallback wrap(AdvertisingSetCallback callback, Handler handler) { return new IAdvertisingSetCallback.Stub() { - public void onAdvertisingSetStarted(int advertiserId, int status) { + public void onAdvertisingSetStarted(int advertiserId, int txPower, int status) { handler.post(new Runnable() { @Override public void run() { if (status != AdvertisingSetCallback.ADVERTISE_SUCCESS) { - callback.onAdvertisingSetStarted(null, status); + callback.onAdvertisingSetStarted(null, 0, status); mCallbackWrappers.remove(callback); return; } @@ -413,7 +413,7 @@ public final class BluetoothLeAdvertiser { AdvertisingSet advertisingSet = new AdvertisingSet(advertiserId, mBluetoothManager); mAdvertisingSets.put(advertiserId, advertisingSet); - callback.onAdvertisingSetStarted(advertisingSet, status); + callback.onAdvertisingSetStarted(advertisingSet, txPower, status); } }); } @@ -460,12 +460,12 @@ public final class BluetoothLeAdvertiser { }); } - public void onAdvertisingParametersUpdated(int advertiserId, int status) { + public void onAdvertisingParametersUpdated(int advertiserId, int txPower, int status) { handler.post(new Runnable() { @Override public void run() { AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId); - callback.onAdvertisingParametersUpdated(advertisingSet, status); + callback.onAdvertisingParametersUpdated(advertisingSet, txPower, status); } }); } diff --git a/core/java/android/bluetooth/le/IAdvertisingSetCallback.aidl b/core/java/android/bluetooth/le/IAdvertisingSetCallback.aidl index 4b0a111fa3d8..e6a09f1d71d6 100644 --- a/core/java/android/bluetooth/le/IAdvertisingSetCallback.aidl +++ b/core/java/android/bluetooth/le/IAdvertisingSetCallback.aidl @@ -20,12 +20,12 @@ package android.bluetooth.le; * @hide */ oneway interface IAdvertisingSetCallback { - void onAdvertisingSetStarted(in int advertiserId, in int status); + void onAdvertisingSetStarted(in int advertiserId, in int tx_power, in int status); void onAdvertisingSetStopped(in int advertiserId); void onAdvertisingEnabled(in int advertiserId, in boolean enable, in int status); void onAdvertisingDataSet(in int advertiserId, in int status); void onScanResponseDataSet(in int advertiserId, in int status); - void onAdvertisingParametersUpdated(in int advertiserId, in int status); + void onAdvertisingParametersUpdated(in int advertiserId, in int tx_power, in int status); void onPeriodicAdvertisingParametersUpdated(in int advertiserId, in int status); void onPeriodicAdvertisingDataSet(in int advertiserId, in int status); void onPeriodicAdvertisingEnable(in int advertiserId, in boolean enable, in int status); diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java index 56f5d4483270..bb844a327168 100644 --- a/core/java/android/companion/AssociationRequest.java +++ b/core/java/android/companion/AssociationRequest.java @@ -23,6 +23,7 @@ import android.os.Parcelable; import android.provider.OneTimeUseBuilder; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.CollectionUtils; import java.util.ArrayList; import java.util.List; @@ -47,7 +48,7 @@ public final class AssociationRequest implements Parcelable { private AssociationRequest( boolean singleDevice, @Nullable List<DeviceFilter<?>> deviceFilters) { this.mSingleDevice = singleDevice; - this.mDeviceFilters = ArrayUtils.emptyIfNull(deviceFilters); + this.mDeviceFilters = CollectionUtils.emptyIfNull(deviceFilters); } private AssociationRequest(Parcel in) { diff --git a/core/java/android/companion/BluetoothDeviceFilter.java b/core/java/android/companion/BluetoothDeviceFilter.java index 0f16b7b90165..1d8df7f26f6e 100644 --- a/core/java/android/companion/BluetoothDeviceFilter.java +++ b/core/java/android/companion/BluetoothDeviceFilter.java @@ -31,6 +31,7 @@ import android.os.ParcelUuid; import android.provider.OneTimeUseBuilder; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.CollectionUtils; import java.util.ArrayList; import java.util.List; @@ -53,8 +54,8 @@ public final class BluetoothDeviceFilter implements DeviceFilter<BluetoothDevice List<ParcelUuid> serviceUuidMasks) { mNamePattern = namePattern; mAddress = address; - mServiceUuids = ArrayUtils.emptyIfNull(serviceUuids); - mServiceUuidMasks = ArrayUtils.emptyIfNull(serviceUuidMasks); + mServiceUuids = CollectionUtils.emptyIfNull(serviceUuids); + mServiceUuidMasks = CollectionUtils.emptyIfNull(serviceUuidMasks); } private BluetoothDeviceFilter(Parcel in) { diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java index fb280a1db28c..7a0158a8d9af 100644 --- a/core/java/android/content/pm/ShortcutManager.java +++ b/core/java/android/content/pm/ShortcutManager.java @@ -39,8 +39,6 @@ import com.android.internal.util.Preconditions; import java.util.List; /** - * <p><strong>TODO Update the overview to how to use the O new features.</strong></p> - * * The ShortcutManager manages an app's <em>shortcuts</em>. Shortcuts provide users * with quick access to activities other than an app's main activity in the currently-active * launcher. For example, @@ -51,20 +49,20 @@ import java.util.List; * <h3>Static Shortcuts and Dynamic Shortcuts</h3> * * <p> - * There are two ways to publish shortcuts: static shortcuts and dynamic shortcuts. + * There are several different types of shortcuts: * * <ul> - * <li>Static shortcuts are declared in a resource - * XML file, which is referenced in the publisher app's <code>AndroidManifest.xml</code> file. - * Static shortcuts are published when an app is installed, - * and the details of these shortcuts change when an app is upgraded with an updated XML - * file. - * Static shortcuts are immutable, and their - * definitions, such as icons and labels, cannot be changed dynamically without upgrading the - * publisher app. - * - * <li>Dynamic shortcuts are published at runtime using this class's APIs. - * Apps can publish, update, and remove dynamic shortcuts at runtime. + * <li><p>Static shortcuts are declared in a resource XML file, which is referenced in the publisher + * app's <code>AndroidManifest.xml</code> file. These shortcuts are visually associated with an + * app's launcher icon. + * <p>Static shortcuts are published when an app is installed, and the details of these shortcuts + * change when an app is upgraded with an updated XML file. Static shortcuts are immutable, and + * their definitions, such as icons and labels, cannot be changed dynamically without upgrading the + * publisher app.</li> + * + * <li>Dynamic shortcuts are published at runtime using this class's APIs. These shortcuts are + * visually associated with an app's launcher icon. Apps can publish, update, and remove dynamic + * shortcuts at runtime. * </ul> * * <p>Only main activities—activities that handle the {@code MAIN} action and the @@ -72,10 +70,8 @@ import java.util.List; * If an app has multiple main activities, these activities have different sets * of shortcuts. * - * <p>Static shortcuts and dynamic shortcuts are shown in the currently active launcher when - * the user long-presses on an app's launcher icon. - * - * <p class="note"><strong>Note: </strong>The actual gesture may be different + * <p>Static shortcuts and dynamic shortcuts are shown in a supported launcher when the user + * long-presses on an app's launcher icon. Note that the actual gesture may be different * depending on the launcher app. * * <p>Each launcher icon can have at most {@link #getMaxShortcutCountPerActivity()} number of @@ -84,18 +80,19 @@ import java.util.List; * * <h3>Pinning Shortcuts</h3> * - * <p> - * Launcher apps allow users to <em>pin</em> shortcuts so they're easier to access. Both static - * and dynamic shortcuts can be pinned. - * Pinned shortcuts <b>cannot</b> be removed by publisher - * apps; they're removed only when the user removes them, - * when the publisher app is uninstalled, or when the - * user performs the <strong>clear data</strong> action on the publisher app from the device's Settings - * app. + * <p>Apps running in the foreground can also <em>pin</em> shortcuts at runtime, subject to user + * permission, using this class's APIs. Each pinned shortcut is a copy of a static shortcut or a + * dynamic shortcut. Although users can pin a shortcut multiple times, the system calls the pinning + * API only once to complete the pinning process. Unlike static and dynamic shortcuts, pinned + * shortcuts appear as separate icons, visually distinct from the app's launcher icon, in the + * launcher. There is no limit to the number of pinned shortcuts that an app can create. * - * <p>However, the publisher app can <em>disable</em> pinned shortcuts so they cannot be - * started. See the following sections for details. + * <p>Pinned shortcuts <strong>cannot</strong> be removed by publisher apps. They're removed only + * when the user removes them, when the publisher app is uninstalled, or when the user performs the + * clear data action on the publisher app from the device's <b>Settings</b> app. * + * <p>However, the publisher app can <em>disable</em> pinned shortcuts so they cannot be started. + * See the following sections for details. * * <h3>Updating and Disabling Shortcuts</h3> * @@ -126,7 +123,7 @@ import java.util.List; * * <li>The app can use {@link #updateShortcuts(List)} to update <em>any</em> of the existing * 8 shortcuts, when, for example, the chat peers' icons have changed. - * </ul> + * </ol> * The {@link #addDynamicShortcuts(List)} and {@link #setDynamicShortcuts(List)} methods * can also be used * to update existing shortcuts with the same IDs, but they <b>cannot</b> be used @@ -135,22 +132,20 @@ import java.util.List; * * * <h4>Disabling Static Shortcuts</h4> - * When an app is upgraded and the new version + * <p>When an app is upgraded and the new version * no longer uses a static shortcut that appeared in the previous version, this deprecated - * shortcut will no longer be published as a static shortcut. + * shortcut isn't published as a static shortcut. * * <p>If the deprecated shortcut is pinned, then the pinned shortcut will remain on the launcher, - * but it will be disabled automatically. - * Note that, in this case, the pinned shortcut is no longer a static shortcut, but it's - * still <b>immutable</b>. Therefore, it cannot be updated using this class's APIs. - * + * but it's disabled automatically. When a pinned shortcut is disabled, this class's APIs cannot + * update it. * * <h4>Disabling Dynamic Shortcuts</h4> * Sometimes pinned shortcuts become obsolete and may not be usable. For example, a pinned shortcut * to a group chat becomes unusable when the associated group chat is deleted. In cases like this, * apps should use {@link #disableShortcuts(List)}, which removes the specified dynamic * shortcuts and also makes any specified pinned shortcuts un-launchable. - * The {@link #disableShortcuts(List, CharSequence)} method can also be used to disabled shortcuts + * The {@link #disableShortcuts(List, CharSequence)} method can also be used to disable shortcuts * and show users a custom error message when they attempt to launch the disabled shortcuts. * * @@ -278,6 +273,104 @@ import java.util.List; *shortcutManager.setDynamicShortcuts(Arrays.asList(shortcut)); * </pre> * + * <h3>Publishing Pinned Shortcuts</h3> + * + * <p>Apps can pin an existing shortcut (either static or dynamic) or an entirely new shortcut to a + * supported launcher programatically using {@link #requestPinShortcut(ShortcutInfo, IntentSender)}. + * You pass two arguments into this method: + * + * <ul> + * <li>A {@link ShortcutInfo} object – If the shortcut already exists, this object should + * contain only the shortcut's ID. Otherwise, the new {@link ShortcutInfo} object must contain an + * ID, an intent, and a short label for the new shortcut. + * <li><p>A {@link android.app.PendingIntent} object – This intent represents the callback + * that your app receives if the shortcut is successfully pinned to the device's launcher. + * <p><b>Note:</b> If the user doesn't allow the shortcut to be pinned to the launcher, the + * pinning process fails, and the {@link Intent} object that is passed into this + * {@link android.app.PendingIntent} object isn't executed. + * </ul> + * + * The following code snippet shows how to pin a single shortcut that already exists and is enabled: + * + * <pre> + *ShortcutManager mShortcutManager = + * context.getSystemService(ShortcutManager.class); + * + *if (mShortcutManager.isRequestPinShortcutSupported()) { + * + * // This example defines a new shortcut; that is, this shortcut hasn't + * // been published before. + * ShortcutInfo pinShortcutInfo = new ShortcutInfo.Builder() + * .setIcon(myIcon) + * .setShortLabel("My awesome shortcut") + * .setIntent(myIntent) + * .build(); + * + * PendingIntent resultPendingIntent = null; + * + * // Create the following Intent and PendingIntent objects only if your app + * // needs to be notified that the user allowed the shortcut to be pinned. + * // Use a boolean value, such as "appNeedsNotifying", to define this behavior. + * if (appNeedsNotifying) { + * // We assume here that the app has implemented a method called + * // createShortcutResultIntent() that returns a broadcast intent. + * Intent pinnedShortcutCallbackIntent = + * createShortcutResultIntent(pinShortcutInfo); + * + * // Configure the intent so that your app's broadcast receiver gets + * // the callback successfully. + * PendingIntent successCallback = PendingIntent.createBroadcast(context, 0, + * pinnedShortcutCallbackIntent); + * + * resultPendingIntent = successCallback.getIntentSender(); + * } + * + * mShortcutManager.requestPinShortcut(pinShortcutInfo, resultPendingIntent); + *} + * </pre> + * + * <p class="note"><strong>Note:</strong> As you add logic in your app to make requests to pin + * shortcuts, keep in mind that not all launchers support pinning of shortcuts. To determine whether + * your app can complete this process on a particular device, check the return value of + * {@link #isRequestPinShortcutSupported()}. Based on this return value, you might decide to hide + * the option in your app that allows users to pin a shortcut. + * + * <h4>Custom Activity for Pinning Shortcuts</h4> + * + * <p>You can also create a specialized activity that helps users create shortcuts, complete with + * custom options and a confirmation button. In your app's manifest file, add + * {@link Intent#ACTION_CREATE_SHORTCUT} to the activity's <code><intent-filter></code> + * element, as shown in the following snippet: + * + * <pre> + *<manifest> + * ... + * <application> + * <activity android:name="com.example.MyCustomPromptToPinShortcut" ... > + * <intent-filter + * action android:name="android.intent.action.ACTION_CREATE_SHORTCUT"> + * ... + * </intent-filter> + * </activity> + * ... + * </application> + *</manifest> + * </pre> + * + * <p>When you use this specialized activity in your app, the following sequence of steps takes + * place:</p> + * + * <ol> + * <li>The user attempts to create a shortcut, triggering the system to start the specialized + * activity.</li> + * <li>The user sets options for the shortcut.</li> + * <li>The user selects the confirmation button, allowing your app to create the shortcut using + * the {@link #createShortcutResultIntent(ShortcutInfo)} method. This method returns an + * {@link Intent}, which your app relays back to the previously-executing activity using + * {@link Activity#setResult(int)}.</li> + * <li>Your app calls {@link Activity#finish()} on the activity used for creating the customized + * shortcut.</li> + * </ol> * * <h3>Shortcut Intents</h3> * <p> @@ -825,7 +918,7 @@ public class ShortcutManager { } /** - * Return {@code TRUE} if the default launcher supports + * Return {@code TRUE} if the app is running on a device whose default launcher supports * {@link #requestPinShortcut(ShortcutInfo, IntentSender)}. */ public boolean isRequestPinShortcutSupported() { @@ -839,29 +932,30 @@ public class ShortcutManager { /** * Request to create a pinned shortcut. The default launcher will receive this request and - * ask the user for approval. If the user approves it, the shortcut will be created and - * {@code resultIntent} will be sent. Otherwise, no responses will be sent to the caller. + * ask the user for approval. If the user approves it, the shortcut will be created, and + * {@code resultIntent} will be sent. If a request is denied by the user, however, no response + * will be sent to the caller. * - * <p>When a request is denied by the user, the caller app will not get any response. + * <p>Only apps with a foreground activity or a foreground service can call this method. + * Otherwise, it'll throw {@link IllegalStateException}. * - * <p>Only apps with a foreground activity or a foreground service can call it. Otherwise - * it'll throw {@link IllegalStateException}. + * <p>It's up to the launcher to decide how to handle previous pending requests when the same + * package calls this API multiple times in a row. One possible strategy is to ignore any + * previous requests. * - * <p>It's up to the launcher how to handle previous pending requests when the same package - * calls this API multiple times in a row. It may ignore the previous requests, - * for example. + * @param shortcut Shortcut to pin. If an app wants to pin an existing (either static + * or dynamic) shortcut, then it only needs to have an ID. Although other fields don't have + * to be set, the target shortcut must be enabled. * - * @param shortcut New shortcut to pin. If an app wants to pin an existing (either dynamic - * or manifest) shortcut, then it only needs to have an ID, and other fields don't have to - * be set, in which case, the target shortcut must be enabled. - * If it's a new shortcut, all the mandatory fields, such as a short label, must be + * <p>If it's a new shortcut, all the mandatory fields, such as a short label, must be * set. * @param resultIntent If not null, this intent will be sent when the shortcut is pinned. - * Use {@link android.app.PendingIntent#getIntentSender()} to create a {@link IntentSender}. + * Use {@link android.app.PendingIntent#getIntentSender()} to create an {@link IntentSender}. * * @return {@code TRUE} if the launcher supports this feature. Note the API will return without * waiting for the user to respond, so getting {@code TRUE} from this API does *not* mean - * the shortcut is pinned. {@code FALSE} if the launcher doesn't support this feature. + * the shortcut was pinned successfully. {@code FALSE} if the launcher doesn't support this + * feature. * * @see #isRequestPinShortcutSupported() * @see IntentSender @@ -869,7 +963,7 @@ public class ShortcutManager { * * @throws IllegalArgumentException if a shortcut with the same ID exists and is disabled. * @throws IllegalStateException The caller doesn't have a foreground activity or a foreground - * service or when the user is locked. + * service, or the device is locked. */ public boolean requestPinShortcut(@NonNull ShortcutInfo shortcut, @Nullable IntentSender resultIntent) { @@ -882,16 +976,17 @@ public class ShortcutManager { } /** - * Returns an Intent which can be used by the default launcher to pin {@param shortcut}. - * This should be used by an Activity to set result in response to - * {@link Intent#ACTION_CREATE_SHORTCUT}. + * Returns an Intent which can be used by the default launcher to pin a shortcut containing the + * given {@link ShortcutInfo}. This method should be used by an Activity to set a result in + * response to {@link Intent#ACTION_CREATE_SHORTCUT}. * * @param shortcut New shortcut to pin. If an app wants to pin an existing (either dynamic * or manifest) shortcut, then it only needs to have an ID, and other fields don't have to * be set, in which case, the target shortcut must be enabled. * If it's a new shortcut, all the mandatory fields, such as a short label, must be * set. - * @return The intent that should be set as the result for the calling activity or null. + * @return The intent that should be set as the result for the calling activity, or + * <code>null</code> if the current launcher doesn't support shortcuts. * * @see Intent#ACTION_CREATE_SHORTCUT * diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index af953e6ce98f..a0448043349d 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -245,9 +245,12 @@ public final class AssetManager implements AutoCloseable { * * @param resId the resource id of the string array */ - final CharSequence[] getResourceTextArray(@ArrayRes int resId) { + final @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) { synchronized (this) { final int[] rawInfoArray = getArrayStringInfo(resId); + if (rawInfoArray == null) { + return null; + } final int rawInfoArrayLen = rawInfoArray.length; final int infoArrayLen = rawInfoArrayLen / 2; int block; diff --git a/core/java/android/net/ConnectivityMetricsEvent.java b/core/java/android/net/ConnectivityMetricsEvent.java index 4faff62c2655..6e468d223d3a 100644 --- a/core/java/android/net/ConnectivityMetricsEvent.java +++ b/core/java/android/net/ConnectivityMetricsEvent.java @@ -76,7 +76,13 @@ public final class ConnectivityMetricsEvent implements Parcelable { @Override public String toString() { - // TODO: add transports, netId, ifname - return String.format("ConnectivityMetricsEvent(%tT.%tL): %s", timestamp, timestamp, data); + StringBuilder buffer = new StringBuilder("ConnectivityMetricsEvent("); + buffer.append(String.format("%tT.%tL", timestamp, timestamp)); + // TODO: add transports, netId + if (ifname != null) { + buffer.append(", ").append(ifname); + } + buffer.append("): ").append(data.toString()); + return buffer.toString(); } } diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index 4dd8ce9c8beb..0765c8677644 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -423,8 +423,10 @@ public final class NetworkCapabilities implements Parcelable { */ public static final int TRANSPORT_WIFI_AWARE = 5; - private static final int MIN_TRANSPORT = TRANSPORT_CELLULAR; - private static final int MAX_TRANSPORT = TRANSPORT_WIFI_AWARE; + /** @hide */ + public static final int MIN_TRANSPORT = TRANSPORT_CELLULAR; + /** @hide */ + public static final int MAX_TRANSPORT = TRANSPORT_WIFI_AWARE; /** * Adds the given transport type to this {@code NetworkCapability} instance. diff --git a/core/java/android/net/metrics/DhcpClientEvent.java b/core/java/android/net/metrics/DhcpClientEvent.java index 7e30ab59ccdf..c5b78a50639d 100644 --- a/core/java/android/net/metrics/DhcpClientEvent.java +++ b/core/java/android/net/metrics/DhcpClientEvent.java @@ -31,25 +31,21 @@ public final class DhcpClientEvent implements Parcelable { /** {@hide} Represents transitions from and to DhcpBoundState via DhcpRenewingState */ public static final String RENEWING_BOUND = "RenewingBoundState"; - public final String ifName; public final String msg; public final int durationMs; - public DhcpClientEvent(String ifName, String msg, int durationMs) { - this.ifName = ifName; + public DhcpClientEvent(String msg, int durationMs) { this.msg = msg; this.durationMs = durationMs; } private DhcpClientEvent(Parcel in) { - this.ifName = in.readString(); this.msg = in.readString(); this.durationMs = in.readInt(); } @Override public void writeToParcel(Parcel out, int flags) { - out.writeString(ifName); out.writeString(msg); out.writeInt(durationMs); } @@ -61,7 +57,7 @@ public final class DhcpClientEvent implements Parcelable { @Override public String toString() { - return String.format("DhcpClientEvent(%s, %s, %dms)", ifName, msg, durationMs); + return String.format("DhcpClientEvent(%s, %dms)", msg, durationMs); } public static final Parcelable.Creator<DhcpClientEvent> CREATOR diff --git a/core/java/android/net/metrics/DhcpErrorEvent.java b/core/java/android/net/metrics/DhcpErrorEvent.java index f34ffdfeb0d7..8b771979bc24 100644 --- a/core/java/android/net/metrics/DhcpErrorEvent.java +++ b/core/java/android/net/metrics/DhcpErrorEvent.java @@ -54,7 +54,6 @@ public final class DhcpErrorEvent implements Parcelable { public static final int RECEIVE_ERROR = makeErrorCode(MISC_ERROR, 2); public static final int PARSING_ERROR = makeErrorCode(MISC_ERROR, 3); - public final String ifName; // error code byte format (MSB to LSB): // byte 0: error type // byte 1: error subtype @@ -62,19 +61,16 @@ public final class DhcpErrorEvent implements Parcelable { // byte 3: optional code public final int errorCode; - public DhcpErrorEvent(String ifName, int errorCode) { - this.ifName = ifName; + public DhcpErrorEvent(int errorCode) { this.errorCode = errorCode; } private DhcpErrorEvent(Parcel in) { - this.ifName = in.readString(); this.errorCode = in.readInt(); } @Override public void writeToParcel(Parcel out, int flags) { - out.writeString(ifName); out.writeInt(errorCode); } @@ -104,7 +100,7 @@ public final class DhcpErrorEvent implements Parcelable { @Override public String toString() { - return String.format("DhcpErrorEvent(%s, %s)", ifName, Decoder.constants.get(errorCode)); + return String.format("DhcpErrorEvent(%s)", Decoder.constants.get(errorCode)); } final static class Decoder { diff --git a/core/java/android/net/metrics/IpConnectivityLog.java b/core/java/android/net/metrics/IpConnectivityLog.java index 79094c02b272..8f5e6739cf4e 100644 --- a/core/java/android/net/metrics/IpConnectivityLog.java +++ b/core/java/android/net/metrics/IpConnectivityLog.java @@ -99,6 +99,18 @@ public class IpConnectivityLog { /** * Log an IpConnectivity event. + * @param ifname the network interface associated with the event. + * @param data is a Parcelable instance representing the event. + * @return true if the event was successfully logged. + */ + public boolean log(String ifname, Parcelable data) { + ConnectivityMetricsEvent ev = makeEv(data); + ev.ifname = ifname; + return log(ev); + } + + /** + * Log an IpConnectivity event. * @param data is a Parcelable instance representing the event. * @return true if the event was successfully logged. */ diff --git a/core/java/android/net/metrics/IpManagerEvent.java b/core/java/android/net/metrics/IpManagerEvent.java index 50dda7cdb5dd..f5aea73cf2aa 100644 --- a/core/java/android/net/metrics/IpManagerEvent.java +++ b/core/java/android/net/metrics/IpManagerEvent.java @@ -47,25 +47,21 @@ public final class IpManagerEvent implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface EventType {} - public final String ifName; public final @EventType int eventType; public final long durationMs; - public IpManagerEvent(String ifName, @EventType int eventType, long duration) { - this.ifName = ifName; + public IpManagerEvent(@EventType int eventType, long duration) { this.eventType = eventType; this.durationMs = duration; } private IpManagerEvent(Parcel in) { - this.ifName = in.readString(); this.eventType = in.readInt(); this.durationMs = in.readLong(); } @Override public void writeToParcel(Parcel out, int flags) { - out.writeString(ifName); out.writeInt(eventType); out.writeLong(durationMs); } @@ -88,8 +84,8 @@ public final class IpManagerEvent implements Parcelable { @Override public String toString() { - return String.format("IpManagerEvent(%s, %s, %dms)", - ifName, Decoder.constants.get(eventType), durationMs); + return String.format("IpManagerEvent(%s, %dms)", + Decoder.constants.get(eventType), durationMs); } final static class Decoder { diff --git a/core/java/android/net/metrics/IpReachabilityEvent.java b/core/java/android/net/metrics/IpReachabilityEvent.java index d69e806f6f22..019c2c5a50e4 100644 --- a/core/java/android/net/metrics/IpReachabilityEvent.java +++ b/core/java/android/net/metrics/IpReachabilityEvent.java @@ -41,7 +41,6 @@ public final class IpReachabilityEvent implements Parcelable { /** Neighbor unreachable notification from kernel, IP provisioning is also lost. */ public static final int PROVISIONING_LOST_ORGANIC = 5 << 8; - public final String ifName; // eventType byte format (MSB to LSB): // byte 0: unused // byte 1: unused @@ -49,19 +48,16 @@ public final class IpReachabilityEvent implements Parcelable { // byte 3: when byte 2 == PROBE, errno code from RTNetlink or IpReachabilityMonitor. public final int eventType; - public IpReachabilityEvent(String ifName, int eventType) { - this.ifName = ifName; + public IpReachabilityEvent(int eventType) { this.eventType = eventType; } private IpReachabilityEvent(Parcel in) { - this.ifName = in.readString(); this.eventType = in.readInt(); } @Override public void writeToParcel(Parcel out, int flags) { - out.writeString(ifName); out.writeInt(eventType); } @@ -97,7 +93,7 @@ public final class IpReachabilityEvent implements Parcelable { int hi = eventType & 0xff00; int lo = eventType & 0x00ff; String eventName = Decoder.constants.get(hi); - return String.format("IpReachabilityEvent(%s, %s:%02x)", ifName, eventName, lo); + return String.format("IpReachabilityEvent(%s:%02x)", eventName, lo); } final static class Decoder { diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java index 7906707c0133..15bd175949c4 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -26,6 +26,7 @@ import java.io.FileOutputStream; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.lang.reflect.Modifier; +import java.util.function.Supplier; /** * Base class for a remotable object, the core part of a lightweight @@ -247,6 +248,36 @@ public class Binder implements IBinder { public static final native void restoreCallingIdentity(long token); /** + * Convenience method for running the provided action enclosed in + * {@link #clearCallingIdentity}/{@link #restoreCallingIdentity} + * + * @hide + */ + public static final void withCleanCallingIdentity(Runnable action) { + long callingIdentity = clearCallingIdentity(); + try { + action.run(); + } finally { + restoreCallingIdentity(callingIdentity); + } + } + + /** + * Convenience method for running the provided action enclosed in + * {@link #clearCallingIdentity}/{@link #restoreCallingIdentity} returning the result + * + * @hide + */ + public static final <T> T withCleanCallingIdentity(Supplier<T> action) { + long callingIdentity = clearCallingIdentity(); + try { + return action.get(); + } finally { + restoreCallingIdentity(callingIdentity); + } + } + + /** * Sets the native thread-local StrictMode policy mask. * * <p>The StrictMode settings are kept in two places: a Java-level diff --git a/core/java/android/os/HandlerThread.java b/core/java/android/os/HandlerThread.java index c43251932eda..a4d5c6f4b202 100644 --- a/core/java/android/os/HandlerThread.java +++ b/core/java/android/os/HandlerThread.java @@ -16,6 +16,9 @@ package android.os; +import android.annotation.NonNull; +import android.annotation.Nullable; + /** * Handy class for starting a new thread that has a looper. The looper can then be * used to create handler classes. Note that start() must still be called. @@ -24,6 +27,7 @@ public class HandlerThread extends Thread { int mPriority; int mTid = -1; Looper mLooper; + private @Nullable Handler mHandler; public HandlerThread(String name) { super(name); @@ -86,6 +90,18 @@ public class HandlerThread extends Thread { } /** + * @return a shared {@link Handler} associated with this thread + * @hide + */ + @NonNull + public Handler getThreadHandler() { + if (mHandler == null) { + mHandler = new Handler(getLooper()); + } + return mHandler; + } + + /** * Quits the handler thread's looper. * <p> * Causes the handler thread's looper to terminate without processing any diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 8fc54a0cd3e1..146d2d3caebc 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -7085,6 +7085,8 @@ public final class Settings { INSTANT_APP_SETTINGS.add(DEFAULT_INPUT_METHOD); INSTANT_APP_SETTINGS.add(ENABLED_INPUT_METHODS); + + INSTANT_APP_SETTINGS.add(ANDROID_ID); } /** @@ -10293,6 +10295,7 @@ public final class Settings { INSTANT_APP_SETTINGS.add(DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES); INSTANT_APP_SETTINGS.add(DEVELOPMENT_FORCE_RTL); INSTANT_APP_SETTINGS.add(EPHEMERAL_COOKIE_MAX_SIZE_BYTES); + INSTANT_APP_SETTINGS.add(AIRPLANE_MODE_ON); } /** diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 583dad48295c..350675f11f29 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -5033,7 +5033,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, try { rawHints = a.getTextArray(attr); - } catch (NullPointerException e) { + } catch (Resources.NotFoundException e) { rawString = getResources().getString(resId); } } else { @@ -7379,7 +7379,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * <p>When implementing this method, subclasses must follow the rules below: * * <ol> - * <li>Also implement {@link #autofill(int, AutofillValue)} to autofill the virtual + * <li>Also implement {@link #autofill(SparseArray)} to autofill the virtual * children. * <li>Call * {@link android.view.autofill.AutofillManager#notifyViewEntered} and @@ -7448,24 +7448,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Automatically fills the content of a virtual view with the {@code value} + * Automatically fills the content of a virtual views. * * <p>See {@link #autofill(AutofillValue)} and * {@link #onProvideAutofillVirtualStructure(ViewStructure, int)} for more info. * - * @param value value to be autofilled. - * @param virtualId id identifying the virtual child inside the custom view. + * @param values map of values to be autofilled, keyed by virtual child id. * * @return {@code true} if the view was successfully autofilled, {@code false} otherwise */ - public boolean autofill(@SuppressWarnings("unused") int virtualId, - @SuppressWarnings("unused") AutofillValue value) { + public boolean autofill( + @NonNull @SuppressWarnings("unused") SparseArray<AutofillValue>values) { return false; } /** * Describes the autofill type that should be used on calls to - * {@link #autofill(AutofillValue)} and {@link #autofill(int, AutofillValue)}. + * {@link #autofill(AutofillValue)} and {@link #autofill(SparseArray)}. * * <p>By default returns {@link #AUTOFILL_TYPE_NONE}, but views should override it (and * {@link #autofill(AutofillValue)} to support the Autofill Framework. diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java index 4168756e04f3..989cb13d3e69 100644 --- a/core/java/android/view/ViewStructure.java +++ b/core/java/android/view/ViewStructure.java @@ -275,7 +275,7 @@ public abstract class ViewStructure { * {@link #addChildCount(int)} and {@link #setChildCount(int)}. * @param virtualId an opaque ID to the Android System (although it could be meaningful to the * {@link View} creating the {@link ViewStructure}), but it's the same id used on - * {@link View#autofill(int, AutofillValue)}. + * {@link View#autofill(android.util.SparseArray)}. * @param flags currently {@code 0}. * * @return Returns an fresh {@link ViewStructure} ready to be filled in. @@ -306,7 +306,7 @@ public abstract class ViewStructure { * {@link #addChildCount(int)} and {@link #setChildCount(int)}. * @param virtualId an opaque ID to the Android System (although it could be meaningful to the * {@link View} creating the {@link ViewStructure}), but it's the same id used on - * {@link View#autofill(int, AutofillValue)}. + * {@link View#autofill(android.util.SparseArray)}. * @param flags currently {@code 0}. * * @return Returns an fresh {@link ViewStructure} ready to be filled in. diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java index d0fbe7c8a666..f4dd5a62112c 100644 --- a/core/java/com/android/internal/util/ArrayUtils.java +++ b/core/java/com/android/internal/util/ArrayUtils.java @@ -31,7 +31,6 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Objects; -import java.util.function.Function; /** * ArrayUtils contains some methods that you can call to find out @@ -237,35 +236,6 @@ public class ArrayUtils { return false; } - @NonNull - public static <T> List<T> filter(@Nullable List<?> list, Class<T> c) { - if (isEmpty(list)) return Collections.emptyList(); - ArrayList<T> result = null; - for (int i = 0; i < list.size(); i++) { - final Object item = list.get(i); - if (c.isInstance(item)) { - result = add(result, (T) item); - } - } - return emptyIfNull(result); - } - - public static <T> boolean any(@Nullable List<T> items, - java.util.function.Predicate<T> predicate) { - return find(items, predicate) != null; - } - - @Nullable - public static <T> T find(@Nullable List<T> items, - java.util.function.Predicate<T> predicate) { - if (isEmpty(items)) return null; - for (int i = 0; i < items.size(); i++) { - final T item = items.get(i); - if (predicate.test(item)) return item; - } - return null; - } - public static long total(@Nullable long[] array) { long total = 0; if (array != null) { @@ -504,29 +474,6 @@ public class ArrayUtils { } } - public static int size(@Nullable Collection<?> cur) { - return cur != null ? cur.size() : 0; - } - - public static @NonNull <I, O> List<O> map(@Nullable List<I> cur, - Function<? super I, ? extends O> f) { - if (cur == null || cur.isEmpty()) return Collections.emptyList(); - final ArrayList<O> result = new ArrayList<>(); - for (int i = 0; i < cur.size(); i++) { - result.add(f.apply(cur.get(i))); - } - return result; - } - - /** - * Returns the given list, or an immutable empty list if the provided list is null - * - * @see Collections#emptyList - */ - public static @NonNull <T> List<T> emptyIfNull(@Nullable List<T> cur) { - return cur == null ? Collections.emptyList() : cur; - } - public static <T> boolean contains(@Nullable Collection<T> cur, T val) { return (cur != null) ? cur.contains(val) : false; } diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java new file mode 100644 index 000000000000..287f68cf5a55 --- /dev/null +++ b/core/java/com/android/internal/util/CollectionUtils.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2017 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.internal.util; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Stream; + +/** + * Utility methods for dealing with (typically {@link Nullable}) {@link Collection}s + * + * Unless a method specifies otherwise, a null value for a collection is treated as an empty + * collection of that type. + */ +public class CollectionUtils { + private CollectionUtils() { /* cannot be instantiated */ } + + /** + * Returns a list of items from the provided list that match the given condition. + * + * This is similar to {@link Stream#filter} but without the overhead of creating an intermediate + * {@link Stream} instance + */ + public static @NonNull <T> List<T> filter(@Nullable List<T> list, + java.util.function.Predicate<? super T> predicate) { + ArrayList<T> result = null; + for (int i = 0; i < size(list); i++) { + final T item = list.get(i); + if (predicate.test(item)) { + result = ArrayUtils.add(result, item); + } + } + return emptyIfNull(result); + } + + /** + * Returns a list of items resulting from applying the given function to each element of the + * provided list. + * + * The resulting list will have the same {@link #size} as the input one. + * + * This is similar to {@link Stream#map} but without the overhead of creating an intermediate + * {@link Stream} instance + */ + public static @NonNull <I, O> List<O> map(@Nullable List<I> cur, + Function<? super I, ? extends O> f) { + if (cur == null || cur.isEmpty()) return Collections.emptyList(); + final ArrayList<O> result = new ArrayList<>(); + for (int i = 0; i < cur.size(); i++) { + result.add(f.apply(cur.get(i))); + } + return result; + } + + /** + * Returns the given list, or an immutable empty list if the provided list is null + * + * This can be used to guaranty null-safety without paying the price of extra allocations + * + * @see Collections#emptyList + */ + public static @NonNull <T> List<T> emptyIfNull(@Nullable List<T> cur) { + return cur == null ? Collections.emptyList() : cur; + } + + /** + * Returns the size of the given list, or 0 if the list is null + */ + public static int size(@Nullable Collection<?> cur) { + return cur != null ? cur.size() : 0; + } + + /** + * Returns the elements of the given list that are of type {@code c} + */ + public static @NonNull <T> List<T> filter(@Nullable List<?> list, Class<T> c) { + if (ArrayUtils.isEmpty(list)) return Collections.emptyList(); + ArrayList<T> result = null; + for (int i = 0; i < list.size(); i++) { + final Object item = list.get(i); + if (c.isInstance(item)) { + result = ArrayUtils.add(result, (T) item); + } + } + return emptyIfNull(result); + } + + /** + * Returns whether there exists at least one element in the list for which + * condition {@code predicate} is true + */ + public static <T> boolean any(@Nullable List<T> items, + java.util.function.Predicate<T> predicate) { + return find(items, predicate) != null; + } + + /** + * Returns the first element from the list for which + * condition {@code predicate} is true, or null if there is no such element + */ + public static @Nullable <T> T find(@Nullable List<T> items, + java.util.function.Predicate<T> predicate) { + if (ArrayUtils.isEmpty(items)) return null; + for (int i = 0; i < items.size(); i++) { + final T item = items.get(i); + if (predicate.test(item)) return item; + } + return null; + } +} diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java index 78e879795c06..4a9a2c55e940 100644 --- a/core/java/com/android/server/BootReceiver.java +++ b/core/java/com/android/server/BootReceiver.java @@ -92,6 +92,13 @@ public class BootReceiver extends BroadcastReceiver { // ro.boottime.init.mount_all. + postfix for mount_all duration private static final String[] MOUNT_DURATION_PROPS_POSTFIX = new String[] { "early", "default", "late" }; + // for reboot, fs shutdown time is recorded in last_kmsg. + private static final String[] LAST_KMSG_FILES = + new String[] { "/sys/fs/pstore/console-ramoops", "/proc/last_kmsg" }; + // first: fs shutdown time in ms, second: umount status defined in init/reboot.h + private static final String LAST_SHUTDOWN_TIME_PATTERN = + "powerctl_shutdown_time_ms:([0-9]+):([0-9]+)"; + private static final int UMOUNT_STATUS_NOT_AVAILABLE = 4; // should match with init/reboot.h @Override public void onReceive(final Context context, Intent intent) { @@ -213,8 +220,11 @@ public class BootReceiver extends BroadcastReceiver { } else { if (db != null) db.addText("SYSTEM_RESTART", headers); } - addFsckErrorsToDropBoxAndLogFsStat(db, timestamps, headers, -LOG_SIZE, "SYSTEM_FSCK"); + // log always available fs_stat last so that logcat collecting tools can wait until + // fs_stat to get all file system metrics. + logFsShutdownTime(); logFsMountTime(); + addFsckErrorsToDropBoxAndLogFsStat(db, timestamps, headers, -LOG_SIZE, "SYSTEM_FSCK"); // Scan existing tombstones (in case any new ones appeared) File[] tombstoneFiles = TOMBSTONE_DIR.listFiles(); @@ -323,7 +333,6 @@ public class BootReceiver extends BroadcastReceiver { if (fileTime <= 0) return; // File does not exist String log = FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n"); - StringBuilder sb = new StringBuilder(); Pattern pattern = Pattern.compile(FS_STAT_PATTERN); for (String line : log.split("\n")) { // should check all lines if (line.contains("FILE SYSTEM WAS MODIFIED")) { @@ -355,6 +364,45 @@ public class BootReceiver extends BroadcastReceiver { } } + private static void logFsShutdownTime() { + File f = null; + for (String fileName : LAST_KMSG_FILES) { + File file = new File(fileName); + if (!file.exists()) continue; + f = file; + break; + } + if (f == null) { // no last_kmsg + return; + } + + final int maxReadSize = 16*1024; + // last_kmsg can be very big, so only parse the last part + String lines; + try { + lines = FileUtils.readTextFile(f, -maxReadSize, null); + } catch (IOException e) { + Slog.w(TAG, "cannot read last msg", e); + return; + } + Pattern pattern = Pattern.compile(LAST_SHUTDOWN_TIME_PATTERN, Pattern.MULTILINE); + Matcher matcher = pattern.matcher(lines); + if (matcher.find()) { + MetricsLogger.histogram(null, "boot_fs_shutdown_duration", + Integer.parseInt(matcher.group(1))); + MetricsLogger.histogram(null, "boot_fs_shutdown_umount_stat", + Integer.parseInt(matcher.group(2))); + Slog.i(TAG, "boot_fs_shutdown," + matcher.group(1) + "," + matcher.group(2)); + } else { // not found + // This can happen when a device has too much kernel log after file system unmount + // ,exceeding maxReadSize. And having that much kernel logging can affect overall + // performance as well. So it is better to fix the kernel to reduce the amount of log. + MetricsLogger.histogram(null, "boot_fs_shutdown_umount_stat", + UMOUNT_STATUS_NOT_AVAILABLE); + Slog.w(TAG, "boot_fs_shutdown, string not found"); + } + } + private static void handleFsckFsStat(Matcher match) { String partition = match.group(1); int stat; diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index c6cd7468e1ea..9b2aba3e1eaf 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1041,7 +1041,7 @@ <!-- Control the behavior when the user long presses the home button. 0 - Nothing - 1 - Recent apps view in SystemUI + 1 - Launch all apps intent 2 - Launch assist intent This needs to match the constants in policy/src/com/android/internal/policy/impl/PhoneWindowManager.java diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index 7b8c22962bcc..5669189434e8 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -115,6 +115,9 @@ <!-- accessibility test permissions --> <uses-permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT" /> + <!-- vr test permissions --> + <uses-permission android:name="android.permission.RESTRICTED_VR_ACCESS" /> + <application android:theme="@style/Theme" android:supportsRtl="true"> <uses-library android:name="android.test.runner" /> <uses-library android:name="org.apache.http.legacy" android:required="false" /> diff --git a/core/tests/coretests/src/android/os/SetPersistentVrThreadTest.java b/core/tests/coretests/src/android/os/SetPersistentVrThreadTest.java new file mode 100644 index 000000000000..920988be2eb3 --- /dev/null +++ b/core/tests/coretests/src/android/os/SetPersistentVrThreadTest.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2017 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 android.os; + +import android.app.ActivityManager; +import android.app.VrManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Process; +import android.provider.Settings; +import android.test.ActivityInstrumentationTestCase2; +import android.test.suitebuilder.annotation.SmallTest; +import android.util.Log; + +/** + * Tests ActivityManager#setPersistentVrThread and ActivityManager#setVrThread's + * interaction with persistent VR mode. + */ +public class SetPersistentVrThreadTest extends ActivityInstrumentationTestCase2<TestVrActivity> { + private TestVrActivity mActivity; + private ActivityManager mActivityManager; + private VrManager mVrManager; + private Context mContext; + private String mOldVrListener; + private ComponentName mRequestedComponent; + private static final int SCHED_OTHER = 0; + private static final int SCHED_FIFO = 1; + private static final int SCHED_RESET_ON_FORK = 0x40000000; + public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners"; + private static final String TAG = "VrSetPersistentFIFOThreadTest"; + + public SetPersistentVrThreadTest() { + super(TestVrActivity.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mContext = getInstrumentation().getTargetContext(); + mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); + mVrManager = (VrManager) mContext.getSystemService(Context.VR_SERVICE); + + mRequestedComponent = new ComponentName(mContext, + TestVrActivity.TestVrListenerService.class); + mOldVrListener = Settings.Secure.getString(mContext.getContentResolver(), + ENABLED_VR_LISTENERS); + Settings.Secure.putString(mContext.getContentResolver(), ENABLED_VR_LISTENERS, + mRequestedComponent.flattenToString()); + mActivity = getActivity(); + } + + @Override + protected void tearDown() throws Exception { + try { + setPersistentVrModeEnabled(false); + } catch (Throwable e) { + // pass + } + Settings.Secure.putString(mContext.getContentResolver(), ENABLED_VR_LISTENERS, + mOldVrListener); + super.tearDown(); + } + + private void setPersistentVrModeEnabled(boolean enable) throws Throwable { + mVrManager.setPersistentVrModeEnabled(enable); + // Allow the system time to send out callbacks for persistent VR mode. + Thread.sleep(200); + } + + @SmallTest + public void testSetPersistentVrThreadAPISuccess() throws Throwable { + if (!mActivity.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE)) { + return; + } + + int vr_thread = 0, policy = 0; + vr_thread = Process.myTid(); + + setPersistentVrModeEnabled(true); + mActivityManager.setPersistentVrThread(vr_thread); + policy = (int) Process.getThreadScheduler(vr_thread); + assertEquals((SCHED_FIFO | SCHED_RESET_ON_FORK), policy); + + // Check that the thread loses priority when persistent mode is disabled. + setPersistentVrModeEnabled(false); + policy = (int) Process.getThreadScheduler(vr_thread); + assertEquals(SCHED_OTHER, policy); + + // Check that disabling VR mode when in persistent mode does not affect the persistent + // thread. + mActivity.setVrModeEnabled(true, mRequestedComponent); + Thread.sleep(200); + setPersistentVrModeEnabled(true); + Thread.sleep(200); + mActivityManager.setPersistentVrThread(vr_thread); + policy = (int) Process.getThreadScheduler(vr_thread); + assertEquals((SCHED_FIFO | SCHED_RESET_ON_FORK), policy); + mActivity.setVrModeEnabled(false, mRequestedComponent); + Thread.sleep(200); + assertEquals((SCHED_FIFO | SCHED_RESET_ON_FORK), policy); + setPersistentVrModeEnabled(false); + policy = (int) Process.getThreadScheduler(vr_thread); + assertEquals(SCHED_OTHER, policy); + } + + @SmallTest + public void testSetPersistentVrThreadAPIFailure() throws Throwable { + if (!mActivity.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE)) { + return; + } + int vr_thread = 0, policy = 0; + vr_thread = Process.myTid(); + mActivityManager.setPersistentVrThread(vr_thread); + policy = (int) Process.getThreadScheduler(vr_thread); + assertEquals(SCHED_OTHER, policy); + } + + @SmallTest + public void testSetVrThreadAPIFailsInPersistentMode() throws Throwable { + if (!mActivity.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE)) { + return; + } + int vr_thread = 0, policy = 0; + mActivity.setVrModeEnabled(true, mRequestedComponent); + vr_thread = Process.myTid(); + + setPersistentVrModeEnabled(true); + mActivityManager.setVrThread(vr_thread); + policy = (int) Process.getThreadScheduler(vr_thread); + assertEquals(SCHED_OTHER, policy); + setPersistentVrModeEnabled(false); + + // When not in persistent mode the API works again. + mActivity.setVrModeEnabled(true, mRequestedComponent); + mActivityManager.setVrThread(vr_thread); + policy = (int) Process.getThreadScheduler(vr_thread); + assertEquals((SCHED_FIFO | SCHED_RESET_ON_FORK), policy); + + // The thread loses priority when persistent mode is disabled. + setPersistentVrModeEnabled(true); + policy = (int) Process.getThreadScheduler(vr_thread); + assertEquals(SCHED_OTHER, policy); + setPersistentVrModeEnabled(false); + } +} diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 812e4d885b39..daf14af87288 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -522,8 +522,10 @@ void SkiaCanvas::drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount, SkDEBUGFAIL("SkScalar must be a float for these conversions to be valid"); #endif const int ptCount = vertexCount >> 1; - mCanvas->drawVertices(vertexMode, ptCount, (SkPoint*)verts, (SkPoint*)texs, - (SkColor*)colors, indices, indexCount, paint); + mCanvas->drawVertices(SkVertices::MakeCopy(vertexMode, ptCount, (SkPoint*)verts, + (SkPoint*)texs, (SkColor*)colors, + indexCount, indices), + SkBlendMode::kModulate, paint); } // ---------------------------------------------------------------------------- @@ -560,23 +562,17 @@ void SkiaCanvas::drawBitmapMesh(Bitmap& hwuiBitmap, int meshWidth, int meshHeigh hwuiBitmap.getSkBitmap(&bitmap); const int ptCount = (meshWidth + 1) * (meshHeight + 1); const int indexCount = meshWidth * meshHeight * 6; - - /* Our temp storage holds 2 or 3 arrays. - texture points [ptCount * sizeof(SkPoint)] - optionally vertex points [ptCount * sizeof(SkPoint)] if we need a - copy to convert from float to fixed - indices [ptCount * sizeof(uint16_t)] - */ - ssize_t storageSize = ptCount * sizeof(SkPoint); // texs[] - storageSize += indexCount * sizeof(uint16_t); // indices[] - - -#ifndef SK_SCALAR_IS_FLOAT - SkDEBUGFAIL("SkScalar must be a float for these conversions to be valid"); -#endif - std::unique_ptr<char[]> storage(new char[storageSize]); - SkPoint* texs = (SkPoint*)storage.get(); - uint16_t* indices = (uint16_t*)(texs + ptCount); + uint32_t flags = SkVertices::kHasTexCoords_BuilderFlag; + if (colors) { + flags |= SkVertices::kHasColors_BuilderFlag; + } + SkVertices::Builder builder(SkCanvas::kTriangles_VertexMode, ptCount, indexCount, flags); + memcpy(builder.positions(), vertices, ptCount * sizeof(SkPoint)); + if (colors) { + memcpy(builder.colors(), colors, ptCount * sizeof(SkColor)); + } + SkPoint* texs = builder.texCoords(); + uint16_t* indices = builder.indices(); // cons up texture coordinates and indices { @@ -625,7 +621,6 @@ void SkiaCanvas::drawBitmapMesh(Bitmap& hwuiBitmap, int meshWidth, int meshHeigh index += 1; } SkASSERT(indexPtr - indices == indexCount); - SkASSERT((char*)indexPtr - (char*)storage.get() == storageSize); } // double-check that we have legal indices @@ -646,9 +641,7 @@ void SkiaCanvas::drawBitmapMesh(Bitmap& hwuiBitmap, int meshWidth, int meshHeigh sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode); tmpPaint.setShader(image->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode)); - mCanvas->drawVertices(SkCanvas::kTriangles_VertexMode, ptCount, (SkPoint*)vertices, - texs, (const SkColor*)colors, indices, - indexCount, tmpPaint); + mCanvas->drawVertices(builder.detach(), SkBlendMode::kModulate, tmpPaint); } void SkiaCanvas::drawNinePatch(Bitmap& hwuiBitmap, const Res_png_9patch& chunk, diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp index 20ca80b95465..f6e92dca2bb9 100644 --- a/libs/hwui/SkiaCanvasProxy.cpp +++ b/libs/hwui/SkiaCanvasProxy.cpp @@ -31,6 +31,7 @@ #include <SkRSXform.h> #include <SkSurface.h> #include <SkTextBlobRunIterator.h> +#include <SkVertices.h> namespace android { namespace uirenderer { @@ -180,20 +181,20 @@ void SkiaCanvasProxy::onDrawImageLattice(const SkImage* image, const Lattice& la } } -void SkiaCanvasProxy::onDrawVertices(VertexMode mode, int vertexCount, const SkPoint vertices[], - const SkPoint texs[], const SkColor colors[], SkBlendMode, const uint16_t indices[], - int indexCount, const SkPaint& paint) { +void SkiaCanvasProxy::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode, + const SkPaint& paint) { // TODO: should we pass through blendmode if (mFilterHwuiCalls) { return; } // convert the SkPoints into floats static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats"); - const int floatCount = vertexCount << 1; - const float* vArray = &vertices[0].fX; - const float* tArray = (texs) ? &texs[0].fX : NULL; - const int* cArray = (colors) ? (int*)colors : NULL; - mCanvas->drawVertices(mode, floatCount, vArray, tArray, cArray, indices, indexCount, paint); + const int floatCount = vertices->vertexCount() << 1; + const float* vArray = (const float*)vertices->positions(); + const float* tArray = (const float*)vertices->texCoords(); + const int* cArray = (const int*)vertices->colors(); + mCanvas->drawVertices(vertices->mode(), floatCount, vArray, tArray, cArray, + vertices->indices(), vertices->indexCount(), paint); } sk_sp<SkSurface> SkiaCanvasProxy::onNewSurface(const SkImageInfo&, const SkSurfaceProps&) { diff --git a/libs/hwui/SkiaCanvasProxy.h b/libs/hwui/SkiaCanvasProxy.h index 3b1dd7383f36..d11a779b3600 100644 --- a/libs/hwui/SkiaCanvasProxy.h +++ b/libs/hwui/SkiaCanvasProxy.h @@ -75,10 +75,7 @@ protected: const SkPaint*); virtual void onDrawImageLattice(const SkImage*, const Lattice& lattice, const SkRect& dst, const SkPaint*); - virtual void onDrawVertices(VertexMode, int vertexCount, const SkPoint vertices[], - const SkPoint texs[], const SkColor colors[], SkBlendMode, - const uint16_t indices[], int indexCount, - const SkPaint&) override; + virtual void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) override; virtual void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override; diff --git a/libs/hwui/tests/unit/FatalTestCanvas.h b/libs/hwui/tests/unit/FatalTestCanvas.h index 4831722b93e6..03d94964ac76 100644 --- a/libs/hwui/tests/unit/FatalTestCanvas.h +++ b/libs/hwui/tests/unit/FatalTestCanvas.h @@ -80,9 +80,7 @@ public: void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) { ADD_FAILURE() << "onDrawPoints not expected in this test"; } - void onDrawVertices(VertexMode, int vertexCount, const SkPoint vertices[], const SkPoint texs[], - const SkColor colors[], SkBlendMode, const uint16_t indices[], int indexCount, - const SkPaint&) { + void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) { ADD_FAILURE() << "onDrawVertices not expected in this test"; } void onDrawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], int count, diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java index 760a2d13eecb..56a573736c31 100755 --- a/media/java/android/mtp/MtpDatabase.java +++ b/media/java/android/mtp/MtpDatabase.java @@ -29,6 +29,7 @@ import android.media.MediaScanner; import android.net.Uri; import android.os.BatteryManager; import android.os.RemoteException; +import android.os.SystemProperties; import android.provider.MediaStore; import android.provider.MediaStore.Audio; import android.provider.MediaStore.Files; @@ -133,6 +134,8 @@ public class MtpDatabase implements AutoCloseable { private int mBatteryLevel; private int mBatteryScale; + private int mDeviceType; + static { System.loadLibrary("media_jni"); } @@ -195,6 +198,7 @@ public class MtpDatabase implements AutoCloseable { } initDeviceProperties(context); + mDeviceType = SystemProperties.getInt("sys.usb.mtp.device_type", 0); mCloseGuard.open("close"); } @@ -710,6 +714,7 @@ public class MtpDatabase implements AutoCloseable { MtpConstants.DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME, MtpConstants.DEVICE_PROPERTY_IMAGE_SIZE, MtpConstants.DEVICE_PROPERTY_BATTERY_LEVEL, + MtpConstants.DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE, }; } @@ -869,6 +874,10 @@ public class MtpDatabase implements AutoCloseable { outStringValue[imageSize.length()] = 0; return MtpConstants.RESPONSE_OK; + case MtpConstants.DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE: + outIntValue[0] = mDeviceType; + return MtpConstants.RESPONSE_OK; + // DEVICE_PROPERTY_BATTERY_LEVEL is implemented in the JNI code default: diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp index 34a7f7ced89f..f7f791696dd3 100644 --- a/media/jni/android_mtp_MtpDatabase.cpp +++ b/media/jni/android_mtp_MtpDatabase.cpp @@ -76,6 +76,7 @@ static jmethodID method_sessionEnded; static jfieldID field_context; static jfieldID field_batteryLevel; static jfieldID field_batteryScale; +static jfieldID field_deviceType; // MtpPropertyList fields static jfieldID field_mCount; @@ -1030,6 +1031,7 @@ static const PropertyTableEntry kDevicePropertyTable[] = { { MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME, MTP_TYPE_STR }, { MTP_DEVICE_PROPERTY_IMAGE_SIZE, MTP_TYPE_STR }, { MTP_DEVICE_PROPERTY_BATTERY_LEVEL, MTP_TYPE_UINT8 }, + { MTP_DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE, MTP_TYPE_UINT32 }, }; bool MyMtpDatabase::getObjectPropertyInfo(MtpObjectProperty property, int& type) { @@ -1209,6 +1211,10 @@ MtpProperty* MyMtpDatabase::getDevicePropertyDesc(MtpDeviceProperty property) { result->setFormRange(0, env->GetIntField(mDatabase, field_batteryScale), 1); result->mCurrentValue.u.u8 = (uint8_t)env->GetIntField(mDatabase, field_batteryLevel); break; + case MTP_DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE: + result = new MtpProperty(property, MTP_TYPE_UINT32); + result->mCurrentValue.u.u32 = (uint32_t)env->GetIntField(mDatabase, field_deviceType); + break; } checkAndClearExceptionFromCallback(env, __FUNCTION__); @@ -1388,6 +1394,11 @@ int register_android_mtp_MtpDatabase(JNIEnv *env) ALOGE("Can't find MtpDatabase.mBatteryScale"); return -1; } + field_deviceType = env->GetFieldID(clazz, "mDeviceType", "I"); + if (field_deviceType == NULL) { + ALOGE("Can't find MtpDatabase.mDeviceType"); + return -1; + } // now set up fields for MtpPropertyList class clazz = env->FindClass("android/mtp/MtpPropertyList"); diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java index e1e60bb99374..e49463f04ec6 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java @@ -35,9 +35,7 @@ import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.companion.AssociationRequest; import android.companion.BluetoothDeviceFilter; -import android.companion.BluetoothDeviceFilterUtils; import android.companion.BluetoothLEDeviceFilter; -import android.companion.CompanionDeviceManager; import android.companion.DeviceFilter; import android.companion.ICompanionDeviceDiscoveryService; import android.companion.ICompanionDeviceDiscoveryServiceCallback; @@ -60,7 +58,7 @@ import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.TextView; -import com.android.internal.util.ArrayUtils; +import com.android.internal.util.CollectionUtils; import com.android.internal.util.Preconditions; import java.util.ArrayList; @@ -185,10 +183,10 @@ public class DeviceDiscoveryService extends Service { mRequest = request; mFilters = request.getDeviceFilters(); - mWifiFilters = ArrayUtils.filter(mFilters, WifiDeviceFilter.class); - mBluetoothFilters = ArrayUtils.filter(mFilters, BluetoothDeviceFilter.class); - mBLEFilters = ArrayUtils.filter(mFilters, BluetoothLEDeviceFilter.class); - mBLEScanFilters = ArrayUtils.map(mBLEFilters, BluetoothLEDeviceFilter::getScanFilter); + mWifiFilters = CollectionUtils.filter(mFilters, WifiDeviceFilter.class); + mBluetoothFilters = CollectionUtils.filter(mFilters, BluetoothDeviceFilter.class); + mBLEFilters = CollectionUtils.filter(mFilters, BluetoothLEDeviceFilter.class); + mBLEScanFilters = CollectionUtils.map(mBLEFilters, BluetoothLEDeviceFilter::getScanFilter); reset(); @@ -357,7 +355,7 @@ public class DeviceDiscoveryService extends Service { public static <T extends Parcelable> DeviceFilterPair<T> findMatch( T dev, @Nullable List<? extends DeviceFilter<T>> filters) { if (isEmpty(filters)) return new DeviceFilterPair<>(dev, null); - final DeviceFilter<T> matchingFilter = ArrayUtils.find(filters, (f) -> f.matches(dev)); + final DeviceFilter<T> matchingFilter = CollectionUtils.find(filters, (f) -> f.matches(dev)); return matchingFilter != null ? new DeviceFilterPair<>(dev, matchingFilter) : null; } diff --git a/packages/ExternalStorageProvider/AndroidManifest.xml b/packages/ExternalStorageProvider/AndroidManifest.xml index 0b290cebf053..1072f95371a0 100644 --- a/packages/ExternalStorageProvider/AndroidManifest.xml +++ b/packages/ExternalStorageProvider/AndroidManifest.xml @@ -9,6 +9,7 @@ <application android:label="@string/app_label"> <provider android:name=".ExternalStorageProvider" + android:label="@string/storage_description" android:authorities="com.android.externalstorage.documents" android:grantUriPermissions="true" android:exported="true" diff --git a/packages/ExternalStorageProvider/res/values/strings.xml b/packages/ExternalStorageProvider/res/values/strings.xml index 8b16d3c3b9d4..324fb59fe497 100644 --- a/packages/ExternalStorageProvider/res/values/strings.xml +++ b/packages/ExternalStorageProvider/res/values/strings.xml @@ -18,6 +18,9 @@ <!-- Title of the external storage application [CHAR LIMIT=32] --> <string name="app_label">External Storage</string> + <!-- Meaningful storage location description shown to client applications [CHAR LIMIT=32] --> + <string name="storage_description">Local storage</string> + <!-- Title for documents backend that offers internal storage. [CHAR LIMIT=24] --> <string name="root_internal_storage">Internal storage</string> <!-- Title for directory in which a user may store their own documents and files. [CHAR LIMIT=24] --> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 1010a8aea2ac..ec52742c36c8 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -73,8 +73,10 @@ <!-- Summary for saved networks --> <string name="saved_network">Saved by <xliff:g id="name">%1$s</xliff:g></string> - <!-- Status message of Wi-Fi when it is connected by a Wi-Fi assistant application. [CHAR LIMIT=NONE] --> - <string name="connected_via_wfa">Connected via Wi\u2011Fi assistant</string> + <!-- Status message of Wi-Fi when it is automatically connected by a network recommendation provider. [CHAR LIMIT=NONE] --> + <string name="connected_via_network_scorer">Automatically connected via %1$s</string> + <!-- Status message of Wi-Fi when it is automatically connected by a default network recommendation provider. [CHAR LIMIT=NONE] --> + <string name="connected_via_network_scorer_default">Automatically connected via Network Quality Scorer</string> <!-- Status message of Wi-Fi when it is connected by Passpoint configuration. [CHAR LIMIT=NONE] --> <string name="connected_via_passpoint">Connected via %1$s</string> <!-- Status message of Wi-Fi when network has matching passpoint credentials. [CHAR LIMIT=NONE] --> diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java index fed48b441b1c..45004c4bc27a 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java @@ -27,6 +27,8 @@ import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; import android.net.NetworkInfo.State; +import android.net.NetworkScoreManager; +import android.net.NetworkScorerAppData; import android.net.ScoredNetwork; import android.net.wifi.IWifiManager; import android.net.wifi.ScanResult; @@ -949,7 +951,15 @@ public class AccessPoint implements Comparable<AccessPoint> { return String.format(format, passpointProvider); } else if (isEphemeral) { // Special case for connected + ephemeral networks. - return context.getString(R.string.connected_via_wfa); + final NetworkScoreManager networkScoreManager = context.getSystemService( + NetworkScoreManager.class); + NetworkScorerAppData scorer = networkScoreManager.getActiveScorer(); + if (scorer != null && scorer.getRecommendationServiceLabel() != null) { + String format = context.getString(R.string.connected_via_network_scorer); + return String.format(format, scorer.getRecommendationServiceLabel()); + } else { + return context.getString(R.string.connected_via_network_scorer_default); + } } } diff --git a/packages/SystemUI/res/drawable/pip_notification_icon.xml b/packages/SystemUI/res/drawable/pip_notification_icon.xml new file mode 100644 index 000000000000..592bc60d553e --- /dev/null +++ b/packages/SystemUI/res/drawable/pip_notification_icon.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (C) 2017 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FF000000" + android:pathData="M11.99,18.54l-7.37,-5.73L3,14.07l9,7 9,-7 -1.63,-1.27 -7.38,5.74zM12,16l7.36,-5.73L21,9l-9,-7 -9,7 1.63,1.27L12,16z"/> +</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 1f88aca8ec59..3e5e586dfbb0 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1765,6 +1765,18 @@ <!-- Label for PIP the drag to close zone [CHAR LIMIT=NONE]--> <string name="pip_phone_close">Close</string> + <!-- Title of menu shown over picture-in-picture. Used for accessibility. --> + <string name="pip_menu_title">Picture in picture menu</string> + + <!-- User visible notification channel name for the PiP BTW notification. [CHAR LIMIT=NONE] --> + <string name="pip_notification_channel_name">Picture-in-picture</string> + + <!-- PiP BTW notification title. [CHAR LIMIT=50] --> + <string name="pip_notification_title"><xliff:g id="name" example="Google Maps">%s</xliff:g> is in picture-in-picture</string> + + <!-- PiP BTW notification description. [CHAR LIMIT=NONE] --> + <string name="pip_notification_message">If you don’t want <xliff:g id="name" example="Google Maps">%s</xliff:g> to use this feature, tap to open settings and turn it off.</string> + <!-- Tuner string --> <string name="change_theme_reboot" translatable="false">Changing the theme requires a restart.</string> <!-- Tuner string --> @@ -1833,9 +1845,6 @@ <!-- App label of the instant apps notification [CHAR LIMIT=60] --> <string name="instant_apps">Instant Apps</string> - <!-- Title of menu shown over picture-in-picture. Used for accessibility. --> - <string name="pip_menu_title">Picture in picture menu</string> - <!-- Message of the instant apps notification indicating they don't need install [CHAR LIMIT=NONE] --> <string name="instant_apps_message">Instant apps don\'t require installation.</string> diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index a549c73c6527..07bd24223104 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -278,7 +278,8 @@ public class KeyguardViewMediator extends SystemUI { */ private static final Intent USER_PRESENT_INTENT = new Intent(Intent.ACTION_USER_PRESENT) .addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING - | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT + | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); /** * {@link #setKeyguardEnabled} waits on this condition when it reenables diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index ecc2fadf5ae2..6cda0766663d 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -56,6 +56,7 @@ public class PipManager implements BasePipManager { private InputConsumerController mInputConsumerController; private PipMenuActivityController mMenuController; private PipMediaController mMediaController; + private PipNotificationController mNotificationController; private PipTouchHandler mTouchHandler; /** @@ -63,13 +64,24 @@ public class PipManager implements BasePipManager { */ TaskStackListener mTaskStackListener = new TaskStackListener() { @Override - public void onActivityPinned() { + public void onActivityPinned(String packageName) { if (!checkCurrentUserId(false /* debug */)) { return; } + mTouchHandler.onActivityPinned(); mMediaController.onActivityPinned(); mMenuController.onActivityPinned(); + mNotificationController.onActivityPinned(packageName); + } + + @Override + public void onActivityUnpinned() { + if (!checkCurrentUserId(false /* debug */)) { + return; + } + + mNotificationController.onActivityUnpinned(); } @Override @@ -160,6 +172,7 @@ public class PipManager implements BasePipManager { mInputConsumerController); mTouchHandler = new PipTouchHandler(context, mActivityManager, mMenuController, mInputConsumerController); + mNotificationController = new PipNotificationController(context, mActivityManager); } /** diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java index 9cb518cfe2e7..e8ba8f3f4125 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java @@ -172,7 +172,6 @@ public class PipMenuActivity extends Activity { updateFromIntent(getIntent()); setTitle(R.string.pip_menu_title); - notifyActivityCallback(mMessenger); } @Override @@ -306,6 +305,7 @@ public class PipMenuActivity extends Activity { private void updateFromIntent(Intent intent) { mToControllerMessenger = intent.getParcelableExtra(EXTRA_CONTROLLER_MESSENGER); + notifyActivityCallback(mMessenger); ParceledListSlice actions = intent.getParcelableExtra(EXTRA_ACTIONS); if (actions != null) { mActions.clear(); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java new file mode 100644 index 000000000000..bdd6b65026f0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2017 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.systemui.pip.phone; + +import static android.app.NotificationManager.IMPORTANCE_MIN; +import static android.app.PendingIntent.FLAG_CANCEL_CURRENT; +import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK; +import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; +import static android.provider.Settings.ACTION_PICTURE_IN_PICTURE_SETTINGS; + +import android.app.IActivityManager; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.Resources; +import android.graphics.drawable.Icon; +import android.net.Uri; +import android.util.Log; + +import com.android.systemui.R; +import com.android.systemui.SystemUI; + +/** + * Manages the BTW notification that shows whenever an activity enters or leaves picture-in-picture. + */ +public class PipNotificationController { + private static final String TAG = PipNotificationController.class.getSimpleName(); + + private static final String CHANNEL_ID = PipNotificationController.class.getName(); + private static final int BTW_NOTIFICATION_ID = 0; + + private Context mContext; + private IActivityManager mActivityManager; + private NotificationManager mNotificationManager; + + public PipNotificationController(Context context, IActivityManager activityManager) { + mContext = context; + mActivityManager = activityManager; + mNotificationManager = NotificationManager.from(context); + createNotificationChannel(); + } + + public void onActivityPinned(String packageName) { + // Clear any existing notification + mNotificationManager.cancel(CHANNEL_ID, BTW_NOTIFICATION_ID); + + // Build a new notification + final Notification.Builder builder = new Notification.Builder(mContext, CHANNEL_ID) + .setLocalOnly(true) + .setOngoing(true) + .setSmallIcon(R.drawable.pip_notification_icon) + .setColor(mContext.getColor( + com.android.internal.R.color.system_notification_accent_color)); + if (updateNotificationForApp(builder, packageName)) { + SystemUI.overrideNotificationAppName(mContext, builder); + + // Show the new notification + mNotificationManager.notify(CHANNEL_ID, BTW_NOTIFICATION_ID, builder.build()); + } + } + + public void onActivityUnpinned() { + ComponentName topPipActivity = PipUtils.getTopPinnedActivity(mContext, mActivityManager); + if (topPipActivity != null) { + onActivityPinned(topPipActivity.getPackageName()); + } else { + mNotificationManager.cancel(CHANNEL_ID, BTW_NOTIFICATION_ID); + } + } + + /** + * Create the notification channel for the PiP BTW notifications if necessary. + */ + private NotificationChannel createNotificationChannel() { + NotificationChannel channel = mNotificationManager.getNotificationChannel(CHANNEL_ID); + if (channel == null) { + channel = new NotificationChannel(CHANNEL_ID, + mContext.getString(R.string.pip_notification_channel_name), IMPORTANCE_MIN); + channel.enableLights(false); + channel.enableVibration(false); + mNotificationManager.createNotificationChannel(channel); + } + return channel; + } + + /** + * Updates the notification builder with app-specific information, returning whether it was + * successful. + */ + private boolean updateNotificationForApp(Notification.Builder builder, String packageName) { + final PackageManager pm = mContext.getPackageManager(); + final ApplicationInfo appInfo; + try { + appInfo = pm.getApplicationInfo(packageName, 0); + } catch (NameNotFoundException e) { + Log.e(TAG, "Could not update notification for application", e); + return false; + } + + if (appInfo != null) { + final String appName = pm.getApplicationLabel(appInfo).toString(); + final String message = mContext.getString(R.string.pip_notification_message, appName); + final Intent settingsIntent = new Intent(ACTION_PICTURE_IN_PICTURE_SETTINGS, + Uri.fromParts("package", packageName, null)); + settingsIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK); + final Icon appIcon = appInfo.icon != 0 + ? Icon.createWithResource(packageName, appInfo.icon) + : Icon.createWithResource(Resources.getSystem(), + com.android.internal.R.drawable.sym_def_app_icon); + + builder.setContentTitle(mContext.getString(R.string.pip_notification_title, appName)) + .setContentText(message) + .setContentIntent(PendingIntent.getActivity(mContext, packageName.hashCode(), + settingsIntent, FLAG_CANCEL_CURRENT)) + .setStyle(new Notification.BigTextStyle().bigText(message)) + .setLargeIcon(appIcon); + return true; + } + return false; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java index b71c87d4cc23..b96b0ae9ddd9 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java @@ -627,7 +627,7 @@ public class PipManager implements BasePipManager { } @Override - public void onActivityPinned() { + public void onActivityPinned(String packageName) { if (DEBUG) Log.d(TAG, "onActivityPinned()"); if (!checkCurrentUserId(DEBUG)) { return; diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index ac24e2e715df..d7c1391af897 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -155,7 +155,8 @@ public class SystemServicesProxy { public abstract static class TaskStackListener { public void onTaskStackChanged() { } public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) { } - public void onActivityPinned() { } + public void onActivityPinned(String packageName) { } + public void onActivityUnpinned() { } public void onPinnedActivityRestartAttempt() { } public void onPinnedStackAnimationStarted() { } public void onPinnedStackAnimationEnded() { } @@ -196,17 +197,22 @@ public class SystemServicesProxy { } @Override - public void onActivityPinned() throws RemoteException { + public void onActivityPinned(String packageName) throws RemoteException { mHandler.removeMessages(H.ON_ACTIVITY_PINNED); - mHandler.sendEmptyMessage(H.ON_ACTIVITY_PINNED); + mHandler.obtainMessage(H.ON_ACTIVITY_PINNED, packageName).sendToTarget(); + } + + @Override + public void onActivityUnpinned() throws RemoteException { + mHandler.removeMessages(H.ON_ACTIVITY_UNPINNED); + mHandler.sendEmptyMessage(H.ON_ACTIVITY_UNPINNED); } @Override public void onPinnedActivityRestartAttempt() throws RemoteException{ mHandler.removeMessages(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT); - mHandler.obtainMessage(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT) - .sendToTarget(); + mHandler.sendEmptyMessage(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT); } @Override @@ -1234,6 +1240,7 @@ public class SystemServicesProxy { private static final int ON_ACTIVITY_DISMISSING_DOCKED_STACK = 7; private static final int ON_TASK_PROFILE_LOCKED = 8; private static final int ON_PINNED_STACK_ANIMATION_STARTED = 9; + private static final int ON_ACTIVITY_UNPINNED = 10; @Override public void handleMessage(Message msg) { @@ -1253,7 +1260,13 @@ public class SystemServicesProxy { } case ON_ACTIVITY_PINNED: { for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { - mTaskStackListeners.get(i).onActivityPinned(); + mTaskStackListeners.get(i).onActivityPinned((String) msg.obj); + } + break; + } + case ON_ACTIVITY_UNPINNED: { + for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { + mTaskStackListeners.get(i).onActivityUnpinned(); } break; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index b7b4a3b6c81d..8da17fa76bd7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -23,6 +23,7 @@ import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; +import com.android.internal.widget.CachingIconView; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.ViewInvertHelper; @@ -126,7 +127,12 @@ public class NotificationShelf extends ActivatableNotificationView implements super.setDark(dark, fade, delay); if (mDark == dark) return; mDark = dark; - mShelfIcons.setDark(dark, fade, delay); + if (fade) { + mViewInvertHelper.fade(dark, delay); + } else { + mViewInvertHelper.update(dark); + } + mShelfIcons.setAmbient(dark); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index ffc4e8d2f018..110170194294 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -27,7 +27,6 @@ import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.drawable.Drawable; @@ -47,7 +46,6 @@ import android.view.animation.Interpolator; import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.Interpolators; import com.android.systemui.R; -import com.android.systemui.statusbar.notification.NotificationIconDozeHelper; import com.android.systemui.statusbar.notification.NotificationUtils; import java.text.NumberFormat; @@ -101,6 +99,7 @@ public class StatusBarIconView extends AnimatedImageView { private int mDensity; private float mIconScale = 1.0f; private final Paint mDotPaint = new Paint(); + private boolean mDotVisible; private float mDotRadius; private int mStaticDotRadius; private int mVisibleState = STATE_ICON; @@ -111,8 +110,6 @@ public class StatusBarIconView extends AnimatedImageView { private OnVisibilityChangedListener mOnVisibilityChangedListener; private int mDrawableColor; private int mIconColor; - private int mDecorColor; - private float mDarkAmount; private ValueAnimator mColorAnimator; private int mCurrentSetColor = NO_COLOR; private int mAnimationStartColor = NO_COLOR; @@ -122,7 +119,6 @@ public class StatusBarIconView extends AnimatedImageView { animation.getAnimatedFraction()); setColorInternal(newColor); }; - private final NotificationIconDozeHelper mDozer; public StatusBarIconView(Context context, String slot, Notification notification) { this(context, slot, notification, false); @@ -131,7 +127,6 @@ public class StatusBarIconView extends AnimatedImageView { public StatusBarIconView(Context context, String slot, Notification notification, boolean blocked) { super(context); - mDozer = new NotificationIconDozeHelper(context); mBlocked = blocked; mSlot = slot; mNumberPain = new Paint(); @@ -195,7 +190,6 @@ public class StatusBarIconView extends AnimatedImageView { public StatusBarIconView(Context context, AttributeSet attrs) { super(context, attrs); - mDozer = new NotificationIconDozeHelper(context); mBlocked = false; mAlwaysScaleIcon = true; updateIconScale(); @@ -472,19 +466,7 @@ public class StatusBarIconView extends AnimatedImageView { * to the drawable. */ public void setDecorColor(int iconTint) { - mDecorColor = iconTint; - updateDecorColor(); - } - - private void updateDecorColor() { - int color = NotificationUtils.interpolateColors(mDecorColor, Color.WHITE, mDarkAmount); - if (mDotPaint.getColor() != color) { - mDotPaint.setColor(color); - - if (mDotAppearAmount != 0) { - invalidate(); - } - } + mDotPaint.setColor(iconTint); } /** @@ -495,7 +477,6 @@ public class StatusBarIconView extends AnimatedImageView { mDrawableColor = color; setColorInternal(color); mIconColor = color; - mDozer.setColor(color); } private void setColorInternal(int color) { @@ -668,14 +649,6 @@ public class StatusBarIconView extends AnimatedImageView { mOnVisibilityChangedListener = listener; } - public void setDark(boolean dark, boolean fade, long delay) { - mDozer.setImageDark(this, dark, fade, delay, mIconColor != NO_COLOR); - mDozer.setIntensityDark(f -> { - mDarkAmount = f; - updateDecorColor(); - }, dark, fade, delay); - } - public interface OnVisibilityChangedListener { void onVisibilityChanged(int newVisibility); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java index bca4b43afc0c..3efa29f87450 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java @@ -18,7 +18,7 @@ package com.android.systemui.statusbar.notification; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.content.Context; +import android.animation.ValueAnimator; import android.graphics.ColorMatrixColorFilter; import android.graphics.Paint; import android.view.View; @@ -38,8 +38,8 @@ public class NotificationCustomViewWrapper extends NotificationViewWrapper { private boolean mIsLegacy; private int mLegacyColor; - protected NotificationCustomViewWrapper(Context ctx, View view, ExpandableNotificationRow row) { - super(ctx, view, row); + protected NotificationCustomViewWrapper(View view, ExpandableNotificationRow row) { + super(view, row); mInvertHelper = new ViewInvertHelper(view, NotificationPanelView.DOZE_ANIMATION_DURATION); mLegacyColor = row.getContext().getColor(R.color.notification_legacy_background_color); } @@ -67,11 +67,13 @@ public class NotificationCustomViewWrapper extends NotificationViewWrapper { } protected void fadeGrayscale(final boolean dark, long delay) { - getDozer().startIntensityAnimation(animation -> { - getDozer().updateGrayscaleMatrix((float) animation.getAnimatedValue()); - mGreyPaint.setColorFilter( - new ColorMatrixColorFilter(getDozer().getGrayscaleColorMatrix())); - mView.setLayerPaint(mGreyPaint); + startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + updateGrayscaleMatrix((float) animation.getAnimatedValue()); + mGreyPaint.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix)); + mView.setLayerPaint(mGreyPaint); + } }, dark, delay, new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -84,9 +86,9 @@ public class NotificationCustomViewWrapper extends NotificationViewWrapper { protected void updateGrayscale(boolean dark) { if (dark) { - getDozer().updateGrayscaleMatrix(1f); + updateGrayscaleMatrix(1f); mGreyPaint.setColorFilter( - new ColorMatrixColorFilter(getDozer().getGrayscaleColorMatrix())); + new ColorMatrixColorFilter(mGrayscaleColorMatrix)); mView.setLayerPaint(mGreyPaint); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java deleted file mode 100644 index d592c5f5b7f3..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2017 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.systemui.statusbar.notification; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ValueAnimator; -import android.graphics.ColorMatrix; -import android.graphics.ColorMatrixColorFilter; -import android.widget.ImageView; - -import com.android.systemui.Interpolators; -import com.android.systemui.statusbar.phone.NotificationPanelView; - -import java.util.function.Consumer; - -public class NotificationDozeHelper { - private final ColorMatrix mGrayscaleColorMatrix = new ColorMatrix(); - - public void fadeGrayscale(final ImageView target, final boolean dark, long delay) { - startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - updateGrayscaleMatrix((float) animation.getAnimatedValue()); - target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix)); - } - }, dark, delay, new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (!dark) { - target.setColorFilter(null); - } - } - }); - } - - public void updateGrayscale(ImageView target, boolean dark) { - if (dark) { - updateGrayscaleMatrix(1f); - target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix)); - } else { - target.setColorFilter(null); - } - } - - public void startIntensityAnimation(ValueAnimator.AnimatorUpdateListener updateListener, - boolean dark, long delay, Animator.AnimatorListener listener) { - float startIntensity = dark ? 0f : 1f; - float endIntensity = dark ? 1f : 0f; - ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity); - animator.addUpdateListener(updateListener); - animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION); - animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); - animator.setStartDelay(delay); - if (listener != null) { - animator.addListener(listener); - } - animator.start(); - } - - public void setIntensityDark(Consumer<Float> listener, boolean dark, - boolean animate, long delay) { - if (animate) { - startIntensityAnimation(a -> listener.accept((Float) a.getAnimatedValue()), dark, delay, - null /* listener */); - } else { - listener.accept(dark ? 1f : 0f); - } - } - - public void updateGrayscaleMatrix(float intensity) { - mGrayscaleColorMatrix.setSaturation(1 - intensity); - } - - public ColorMatrix getGrayscaleColorMatrix() { - return mGrayscaleColorMatrix; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java index 1ffc94480dab..38e4ec1a4d7f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java @@ -16,10 +16,17 @@ package com.android.systemui.statusbar.notification; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; import android.app.Notification; import android.content.Context; +import android.graphics.Color; import android.graphics.ColorFilter; +import android.graphics.ColorMatrixColorFilter; +import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; +import android.graphics.drawable.Drawable; import android.util.ArraySet; import android.view.NotificationHeaderView; import android.view.View; @@ -30,6 +37,7 @@ import android.widget.ImageView; import android.widget.TextView; import com.android.systemui.Interpolators; +import com.android.systemui.R; import com.android.systemui.ViewInvertHelper; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.TransformableView; @@ -47,6 +55,10 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { private static final Interpolator LOW_PRIORITY_HEADER_CLOSE = new PathInterpolator(0.4f, 0f, 0.7f, 1f); + private final PorterDuffColorFilter mIconColorFilter = new PorterDuffColorFilter( + 0, PorterDuff.Mode.SRC_ATOP); + private final int mIconDarkAlpha; + private final int mIconDarkColor = 0xffffffff; protected final ViewInvertHelper mInvertHelper; protected final ViewTransformationHelper mTransformationHelper; @@ -62,7 +74,8 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { private boolean mTransformLowPriorityTitle; protected NotificationHeaderViewWrapper(Context ctx, View view, ExpandableNotificationRow row) { - super(ctx, view, row); + super(view, row); + mIconDarkAlpha = ctx.getResources().getInteger(R.integer.doze_small_icon_alpha); mInvertHelper = new ViewInvertHelper(ctx, NotificationPanelView.DOZE_ANIMATION_DURATION); mTransformationHelper = new ViewTransformationHelper(); @@ -95,16 +108,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { updateInvertHelper(); } - @Override - protected NotificationDozeHelper createDozer(Context ctx) { - return new NotificationIconDozeHelper(ctx); - } - - @Override - protected NotificationIconDozeHelper getDozer() { - return (NotificationIconDozeHelper) super.getDozer(); - } - protected void resolveHeaderViews() { mIcon = (ImageView) mView.findViewById(com.android.internal.R.id.icon); mHeaderText = (TextView) mView.findViewById(com.android.internal.R.id.header_text); @@ -113,7 +116,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { mColor = resolveColor(mExpandButton); mNotificationHeader = (NotificationHeaderView) mView.findViewById( com.android.internal.R.id.notification_header); - getDozer().setColor(mColor); } private int resolveColor(ImageView icon) { @@ -221,8 +223,90 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { // It also may lead to bugs where the icon isn't correctly greyed out. boolean hadColorFilter = mNotificationHeader.getOriginalIconColor() != NotificationHeaderView.NO_COLOR; + if (fade) { + if (hadColorFilter) { + fadeIconColorFilter(mIcon, dark, delay); + fadeIconAlpha(mIcon, dark, delay); + } else { + fadeGrayscale(mIcon, dark, delay); + } + } else { + if (hadColorFilter) { + updateIconColorFilter(mIcon, dark); + updateIconAlpha(mIcon, dark); + } else { + updateGrayscale(mIcon, dark); + } + } + } + } + + private void fadeIconColorFilter(final ImageView target, boolean dark, long delay) { + startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + updateIconColorFilter(target, (Float) animation.getAnimatedValue()); + } + }, dark, delay, null /* listener */); + } + + private void fadeIconAlpha(final ImageView target, boolean dark, long delay) { + startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float t = (float) animation.getAnimatedValue(); + target.setImageAlpha((int) (255 * (1f - t) + mIconDarkAlpha * t)); + } + }, dark, delay, null /* listener */); + } + + protected void fadeGrayscale(final ImageView target, final boolean dark, long delay) { + startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + updateGrayscaleMatrix((float) animation.getAnimatedValue()); + target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix)); + } + }, dark, delay, new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (!dark) { + target.setColorFilter(null); + } + } + }); + } + + private void updateIconColorFilter(ImageView target, boolean dark) { + updateIconColorFilter(target, dark ? 1f : 0f); + } - getDozer().setImageDark(mIcon, dark, fade, delay, !hadColorFilter); + private void updateIconColorFilter(ImageView target, float intensity) { + int color = interpolateColor(mColor, mIconDarkColor, intensity); + mIconColorFilter.setColor(color); + Drawable iconDrawable = target.getDrawable(); + + // Also, the notification might have been modified during the animation, so background + // might be null here. + if (iconDrawable != null) { + Drawable d = iconDrawable.mutate(); + // DrawableContainer ignores the color filter if it's already set, so clear it first to + // get it set and invalidated properly. + d.setColorFilter(null); + d.setColorFilter(mIconColorFilter); + } + } + + private void updateIconAlpha(ImageView target, boolean dark) { + target.setImageAlpha(dark ? mIconDarkAlpha : 255); + } + + protected void updateGrayscale(ImageView target, boolean dark) { + if (dark) { + updateGrayscaleMatrix(1f); + target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix)); + } else { + target.setColorFilter(null); } } @@ -232,6 +316,22 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { mNotificationHeader.setOnClickListener(expandable ? onClickListener : null); } + private static int interpolateColor(int source, int target, float t) { + int aSource = Color.alpha(source); + int rSource = Color.red(source); + int gSource = Color.green(source); + int bSource = Color.blue(source); + int aTarget = Color.alpha(target); + int rTarget = Color.red(target); + int gTarget = Color.green(target); + int bTarget = Color.blue(target); + return Color.argb( + (int) (aSource * (1f - t) + aTarget * t), + (int) (rSource * (1f - t) + rTarget * t), + (int) (gSource * (1f - t) + gTarget * t), + (int) (bSource * (1f - t) + bTarget * t)); + } + @Override public NotificationHeaderView getNotificationHeader() { return mNotificationHeader; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationIconDozeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationIconDozeHelper.java deleted file mode 100644 index 9f79ef2491d9..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationIconDozeHelper.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2017 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.systemui.statusbar.notification; - -import android.content.Context; -import android.graphics.Color; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffColorFilter; -import android.graphics.drawable.Drawable; -import android.widget.ImageView; - -import com.android.systemui.R; - -public class NotificationIconDozeHelper extends NotificationDozeHelper { - - private final int mImageDarkAlpha; - private final int mImageDarkColor = 0xffffffff; - private final PorterDuffColorFilter mImageColorFilter = new PorterDuffColorFilter( - 0, PorterDuff.Mode.SRC_ATOP); - - private int mColor = Color.BLACK; - - public NotificationIconDozeHelper(Context ctx) { - mImageDarkAlpha = ctx.getResources().getInteger(R.integer.doze_small_icon_alpha); - } - - public void setColor(int color) { - mColor = color; - } - - public void setImageDark(ImageView target, boolean dark, boolean fade, long delay, - boolean useGrayscale) { - if (fade) { - if (!useGrayscale) { - fadeImageColorFilter(target, dark, delay); - fadeImageAlpha(target, dark, delay); - } else { - fadeGrayscale(target, dark, delay); - } - } else { - if (!useGrayscale) { - updateImageColorFilter(target, dark); - updateImageAlpha(target, dark); - } else { - updateGrayscale(target, dark); - } - } - } - - private void fadeImageColorFilter(final ImageView target, boolean dark, long delay) { - startIntensityAnimation(animation -> { - updateImageColorFilter(target, (Float) animation.getAnimatedValue()); - }, dark, delay, null /* listener */); - } - - private void fadeImageAlpha(final ImageView target, boolean dark, long delay) { - startIntensityAnimation(animation -> { - float t = (float) animation.getAnimatedValue(); - target.setImageAlpha((int) (255 * (1f - t) + mImageDarkAlpha * t)); - }, dark, delay, null /* listener */); - } - - private void updateImageColorFilter(ImageView target, boolean dark) { - updateImageColorFilter(target, dark ? 1f : 0f); - } - - private void updateImageColorFilter(ImageView target, float intensity) { - int color = NotificationUtils.interpolateColors(mColor, mImageDarkColor, intensity); - mImageColorFilter.setColor(color); - Drawable imageDrawable = target.getDrawable(); - - // Also, the notification might have been modified during the animation, so background - // might be null here. - if (imageDrawable != null) { - Drawable d = imageDrawable.mutate(); - // DrawableContainer ignores the color filter if it's already set, so clear it first to - // get it set and invalidated properly. - d.setColorFilter(null); - d.setColorFilter(mImageColorFilter); - } - } - - private void updateImageAlpha(ImageView target, boolean dark) { - target.setImageAlpha(dark ? mImageDarkAlpha : 255); - } - -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java index f0b6b2e89e9f..846d03ac74dd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification; +import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Color; import android.service.notification.StatusBarNotification; @@ -45,8 +46,7 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp private int mContentHeight; private int mMinHeightHint; - protected NotificationTemplateViewWrapper(Context ctx, View view, - ExpandableNotificationRow row) { + protected NotificationTemplateViewWrapper(Context ctx, View view, ExpandableNotificationRow row) { super(ctx, view, row); mTransformationHelper.setCustomTransformation( new ViewTransformationHelper.CustomTransformation() { @@ -154,20 +154,16 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp // This also clears the existing types super.updateTransformedTypes(); if (mTitle != null) { - mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE, - mTitle); + mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE, mTitle); } if (mText != null) { - mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TEXT, - mText); + mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TEXT, mText); } if (mPicture != null) { - mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_IMAGE, - mPicture); + mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_IMAGE, mPicture); } if (mProgressBar != null) { - mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_PROGRESS, - mProgressBar); + mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_PROGRESS, mProgressBar); } } @@ -177,7 +173,7 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp return; } super.setDark(dark, fade, delay); - setPictureDark(dark, fade, delay); + setPictureGrayscale(dark, fade, delay); setProgressBarDark(dark, fade, delay); } @@ -192,9 +188,12 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp } private void fadeProgressDark(final ProgressBar target, final boolean dark, long delay) { - getDozer().startIntensityAnimation(animation -> { - float t = (float) animation.getAnimatedValue(); - updateProgressDark(target, t); + startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float t = (float) animation.getAnimatedValue(); + updateProgressDark(target, t); + } }, dark, delay, null /* listener */); } @@ -208,9 +207,13 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp updateProgressDark(target, dark ? 1f : 0f); } - private void setPictureDark(boolean dark, boolean fade, long delay) { + protected void setPictureGrayscale(boolean grayscale, boolean fade, long delay) { if (mPicture != null) { - getDozer().setImageDark(mPicture, dark, fade, delay, true /* useGrayscale */); + if (fade) { + fadeGrayscale(mPicture, grayscale, delay); + } else { + updateGrayscale(mPicture, grayscale); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java index c86616bc31ed..c85e8d853b0d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java @@ -16,17 +16,24 @@ package com.android.systemui.statusbar.notification; +import android.animation.Animator; +import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Color; +import android.graphics.ColorMatrix; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import android.service.notification.StatusBarNotification; import android.support.v4.graphics.ColorUtils; import android.view.NotificationHeaderView; import android.view.View; +import com.android.systemui.Interpolators; +import com.android.systemui.R; import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.TransformableView; +import com.android.systemui.statusbar.phone.NotificationPanelView; /** * Wraps the actual notification content view; used to implement behaviors which are different for @@ -34,14 +41,14 @@ import com.android.systemui.statusbar.TransformableView; */ public abstract class NotificationViewWrapper implements TransformableView { + protected final ColorMatrix mGrayscaleColorMatrix = new ColorMatrix(); protected final View mView; protected final ExpandableNotificationRow mRow; - private final NotificationDozeHelper mDozer; - protected boolean mDark; private int mBackgroundColor = 0; protected boolean mShouldInvertDark; protected boolean mDarkInitialized = false; + private boolean mForcedInvisible; public static NotificationViewWrapper wrap(Context ctx, View v, ExpandableNotificationRow row) { if (v.getId() == com.android.internal.R.id.status_bar_latest_event_content) { @@ -58,22 +65,13 @@ public abstract class NotificationViewWrapper implements TransformableView { } else if (v instanceof NotificationHeaderView) { return new NotificationHeaderViewWrapper(ctx, v, row); } else { - return new NotificationCustomViewWrapper(ctx, v, row); + return new NotificationCustomViewWrapper(v, row); } } - protected NotificationViewWrapper(Context ctx, View view, ExpandableNotificationRow row) { + protected NotificationViewWrapper(View view, ExpandableNotificationRow row) { mView = view; mRow = row; - mDozer = createDozer(ctx); - } - - protected NotificationDozeHelper createDozer(Context ctx) { - return new NotificationIconDozeHelper(mView.getContext()); - } - - protected NotificationDozeHelper getDozer() { - return mDozer; } /** @@ -114,6 +112,26 @@ public abstract class NotificationViewWrapper implements TransformableView { || ColorUtils.calculateLuminance(backgroundColor) > 0.5; } + + protected void startIntensityAnimation(ValueAnimator.AnimatorUpdateListener updateListener, + boolean dark, long delay, Animator.AnimatorListener listener) { + float startIntensity = dark ? 0f : 1f; + float endIntensity = dark ? 1f : 0f; + ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity); + animator.addUpdateListener(updateListener); + animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION); + animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); + animator.setStartDelay(delay); + if (listener != null) { + animator.addListener(listener); + } + animator.start(); + } + + protected void updateGrayscaleMatrix(float intensity) { + mGrayscaleColorMatrix.setSaturation(1 - intensity); + } + /** * Update the appearance of the expand button. * diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java index dee15d8163a6..3706dc8242b9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -95,7 +95,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { private int mActualLayoutWidth = NO_VALUE; private float mActualPaddingEnd = NO_VALUE; private float mActualPaddingStart = NO_VALUE; - private boolean mDark; + private boolean mCentered; private boolean mChangingViewPositions; private int mAddAnimationStartIndex = -1; private int mCannedAnimationStartIndex = -1; @@ -183,9 +183,6 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { mAddAnimationStartIndex = Math.min(mAddAnimationStartIndex, childIndex); } } - if (mDark && child instanceof StatusBarIconView) { - ((StatusBarIconView) child).setDark(mDark, false, 0); - } } @Override @@ -315,8 +312,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { numDots++; } } - boolean center = mDark; - if (center && translationX < getLayoutEnd()) { + if (mCentered && translationX < getLayoutEnd()) { float delta = (getLayoutEnd() - translationX) / 2; for (int i = 0; i < childCount; i++) { View view = getChildAt(i); @@ -394,15 +390,9 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { mChangingViewPositions = changingViewPositions; } - public void setDark(boolean dark, boolean fade, long delay) { - mDark = dark; + public void setAmbient(boolean ambient) { + mCentered = ambient; mDisallowNextAnimation = true; - for (int i = 0; i < getChildCount(); i++) { - View view = getChildAt(i); - if (view instanceof StatusBarIconView) { - ((StatusBarIconView) view).setDark(dark, fade, delay); - } - } } public IconState getIconState(StatusBarIconView icon) { diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index f06c8dcf887c..fb714b9b191a 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -3345,11 +3345,11 @@ message MetricsEvent { // CATEGORY: SETTINGS SETTINGS_MANAGE_PICTURE_IN_PICTURE = 812; - // ACTION: Allow "Enable picture-in-picture on hide" for an app - APP_PICTURE_IN_PICTURE_ON_HIDE_ALLOW = 813; + // ACTION: Allow "Enable picture-in-picture" for an app + APP_PICTURE_IN_PICTURE_ALLOW = 813; - // ACTION: Deny "Enable picture-in-picture on hide" for an app - APP_PICTURE_IN_PICTURE_ON_HIDE_DENY = 814; + // ACTION: Deny "Enable picture-in-picture" for an app + APP_PICTURE_IN_PICTURE_DENY = 814; // OPEN: Settings > Language & input > Text-to-speech output -> Speech rate & pitch // CATEGORY: SETTINGS diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 179b3d027289..c6af2903453c 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -954,10 +954,12 @@ class AlarmManagerService extends SystemService { mTimeTickSender = PendingIntent.getBroadcastAsUser(getContext(), 0, new Intent(Intent.ACTION_TIME_TICK).addFlags( Intent.FLAG_RECEIVER_REGISTERED_ONLY - | Intent.FLAG_RECEIVER_FOREGROUND), 0, + | Intent.FLAG_RECEIVER_FOREGROUND + | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS), 0, UserHandle.ALL); Intent intent = new Intent(Intent.ACTION_DATE_CHANGED); - intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); + intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING + | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); mDateChangeSender = PendingIntent.getBroadcastAsUser(getContext(), 0, intent, Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT, UserHandle.ALL); @@ -1034,7 +1036,8 @@ class AlarmManagerService extends SystemService { if (timeZoneWasChanged) { Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED); - intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); + intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING + | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); intent.putExtra("time-zone", zone.getID()); getContext().sendBroadcastAsUser(intent, UserHandle.ALL); } @@ -2518,7 +2521,8 @@ class AlarmManagerService extends SystemService { Intent intent = new Intent(Intent.ACTION_TIME_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT - | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); + | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND + | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); getContext().sendBroadcastAsUser(intent, UserHandle.ALL); // The world has changed on us, so we need to re-evaluate alarms diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index a4f9f21cc147..d02b72660709 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -3085,7 +3085,17 @@ public class ConnectivityService extends IConnectivityManager.Stub boolean tetherEnabledInSettings = (Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.TETHER_SUPPORTED, defaultVal) != 0) && !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING); - return tetherEnabledInSettings && mUserManager.isAdminUser() && + + // Elevate to system UID to avoid caller requiring MANAGE_USERS permission. + boolean adminUser = false; + final long token = Binder.clearCallingIdentity(); + try { + adminUser = mUserManager.isAdminUser(); + } finally { + Binder.restoreCallingIdentity(token); + } + + return tetherEnabledInSettings && adminUser && mTethering.hasTetherableConfiguration(); } diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java index 44ca6a916c47..3e2dae55988c 100644 --- a/services/core/java/com/android/server/DeviceIdleController.java +++ b/services/core/java/com/android/server/DeviceIdleController.java @@ -1136,6 +1136,9 @@ public class DeviceIdleController extends SystemService private final class BinderService extends IDeviceIdleController.Stub { @Override public void addPowerSaveWhitelistApp(String name) { + if (DEBUG) { + Slog.i(TAG, "addPowerSaveWhitelistApp(name = " + name + ")"); + } getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); long ident = Binder.clearCallingIdentity(); @@ -1147,6 +1150,9 @@ public class DeviceIdleController extends SystemService } @Override public void removePowerSaveWhitelistApp(String name) { + if (DEBUG) { + Slog.i(TAG, "removePowerSaveWhitelistApp(name = " + name + ")"); + } getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); long ident = Binder.clearCallingIdentity(); diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java index c946d0937017..2067620d83bf 100644 --- a/services/core/java/com/android/server/LockSettingsService.java +++ b/services/core/java/com/android/server/LockSettingsService.java @@ -146,7 +146,6 @@ public class LockSettingsService extends ILockSettings.Stub { private final LockPatternUtils mLockPatternUtils; private final NotificationManager mNotificationManager; private final UserManager mUserManager; - private final DevicePolicyManager mDevicePolicyManager; private final IActivityManager mActivityManager; private final KeyStore mKeyStore; @@ -385,7 +384,6 @@ public class LockSettingsService extends ILockSettings.Stub { mStorage = injector.getStorage(); mNotificationManager = injector.getNotificationManager(); mUserManager = injector.getUserManager(); - mDevicePolicyManager = injector.getDevicePolicyManager(); mStrongAuthTracker = injector.getStrongAuthTracker(); mStrongAuthTracker.register(mStrongAuth); @@ -2214,20 +2212,21 @@ public class LockSettingsService extends ILockSettings.Stub { Slog.i(TAG, "Managed profile can have escrow token"); return; } + DevicePolicyManager dpm = mInjector.getDevicePolicyManager(); // Devices with Device Owner should have escrow enabled on all users. - if (mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser() != null) { + if (dpm.getDeviceOwnerComponentOnAnyUser() != null) { Slog.i(TAG, "Corp-owned device can have escrow token"); return; } // We could also have a profile owner on the given (non-managed) user for unicorn cases - if (mDevicePolicyManager.getProfileOwnerAsUser(userId) != null) { + if (dpm.getProfileOwnerAsUser(userId) != null) { Slog.i(TAG, "User with profile owner can have escrow token"); return; } // If the device is yet to be provisioned (still in SUW), there is still // a chance that Device Owner will be set on the device later, so postpone // disabling escrow token for now. - if (!mDevicePolicyManager.isDeviceProvisioned()) { + if (!dpm.isDeviceProvisioned()) { Slog.i(TAG, "Postpone disabling escrow tokens until device is provisioned"); return; } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index b40e70948c1a..38b0a30085d6 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -368,6 +368,7 @@ import com.android.server.firewall.IntentFirewall; import com.android.server.pm.Installer; import com.android.server.pm.Installer.InstallerException; import com.android.server.statusbar.StatusBarManagerInternal; +import com.android.server.vr.PersistentVrStateListener; import com.android.server.vr.VrManagerInternal; import com.android.server.wm.WindowManagerService; @@ -593,7 +594,70 @@ public class ActivityManagerService extends IActivityManager.Stub // default action automatically. Important for devices without direct input // devices. private boolean mShowDialogs = true; - private boolean mInVrMode = false; + // VR state flags. + static final int NON_VR_MODE = 0; + static final int VR_MODE = 1; + static final int PERSISTENT_VR_MODE = 2; + private int mVrState = NON_VR_MODE; + private int mTopAppVrThreadTid = 0; + private int mPersistentVrThreadTid = 0; + final PersistentVrStateListener mPersistentVrModeListener = + new PersistentVrStateListener() { + @Override + public void onPersistentVrStateChanged(boolean enabled) { + synchronized(ActivityManagerService.this) { + // There are 4 possible cases here: + // + // Cases for enabled == true + // Invariant: mVrState != PERSISTENT_VR_MODE; + // This is guaranteed as only this function sets mVrState to PERSISTENT_VR_MODE + // Invariant: mPersistentVrThreadTid == 0 + // This is guaranteed by VrManagerService, which only emits callbacks when the + // mode changes, and in setPersistentVrThread, which only sets + // mPersistentVrThreadTid when mVrState = PERSISTENT_VR_MODE + // Case 1: mTopAppVrThreadTid > 0 (someone called setVrThread successfully and is + // the top-app) + // We reset the app which currently has SCHED_FIFO (mPersistentVrThreadTid) to + // SCHED_OTHER + // Case 2: mTopAppVrThreadTid == 0 + // Do nothing + // + // Cases for enabled == false + // Invariant: mVrState == PERSISTENT_VR_MODE; + // This is guaranteed by VrManagerService, which only emits callbacks when the + // mode changes, and the only other assignment of mVrState outside of this + // function checks if mVrState != PERSISTENT_VR_MODE + // Invariant: mTopAppVrThreadTid == 0 + // This is guaranteed in that mTopAppVrThreadTid is only set to a tid when + // mVrState is VR_MODE, and is explicitly set to 0 when setPersistentVrThread is + // called + // mPersistentVrThreadTid > 0 (someone called setPersistentVrThread successfully) + // 3. Reset mPersistentVrThreadTidto SCHED_OTHER + // mPersistentVrThreadTid == 0 + // 4. Do nothing + if (enabled) { + mVrState = PERSISTENT_VR_MODE; + } else { + // Leaving persistent mode implies leaving VR mode. + mVrState = NON_VR_MODE; + } + + if (mVrState == PERSISTENT_VR_MODE) { + if (mTopAppVrThreadTid > 0) { + // Ensure that when entering persistent VR mode the last top-app loses + // SCHED_FIFO. + Process.setThreadScheduler(mTopAppVrThreadTid, Process.SCHED_OTHER, 0); + mTopAppVrThreadTid = 0; + } + } else if (mPersistentVrThreadTid > 0) { + // Ensure that when leaving persistent VR mode we reschedule the high priority + // persistent thread. + Process.setThreadScheduler(mPersistentVrThreadTid, Process.SCHED_OTHER, 0); + mPersistentVrThreadTid = 0; + } + } + } + }; // Whether we should use SCHED_FIFO for UI and RenderThreads. private boolean mUseFifoUiScheduling = false; @@ -2325,28 +2389,36 @@ public class ActivityManagerService extends IActivityManager.Stub } final ActivityRecord r = (ActivityRecord) msg.obj; boolean vrMode; + boolean inVrMode; ComponentName requestedPackage; ComponentName callingPackage; int userId; synchronized (ActivityManagerService.this) { vrMode = r.requestedVrComponent != null; + inVrMode = mVrState != NON_VR_MODE; requestedPackage = r.requestedVrComponent; userId = r.userId; callingPackage = r.info.getComponentName(); - if (mInVrMode != vrMode) { - mInVrMode = vrMode; - mShowDialogs = shouldShowDialogs(getGlobalConfiguration(), mInVrMode); + if (vrMode != inVrMode) { + // Don't change state if we're in persistent VR mode, but do update thread + // priorities if necessary. + if (mVrState != PERSISTENT_VR_MODE) { + mVrState = vrMode ? VR_MODE : NON_VR_MODE; + } + mShowDialogs = shouldShowDialogs(getGlobalConfiguration(), vrMode); if (r.app != null) { ProcessRecord proc = r.app; if (proc.vrThreadTid > 0) { if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) { try { - if (mInVrMode == true) { + if (mVrState == VR_MODE) { Process.setThreadScheduler(proc.vrThreadTid, Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1); + mTopAppVrThreadTid = proc.vrThreadTid; } else { Process.setThreadScheduler(proc.vrThreadTid, Process.SCHED_OTHER, 0); + mTopAppVrThreadTid = 0; } } catch (IllegalArgumentException e) { Slog.w(TAG, "Failed to set scheduling policy, thread does" @@ -13010,51 +13082,98 @@ public class ActivityManagerService extends IActivityManager.Stub } } + @Override public void setVrThread(int tid) { if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) { throw new UnsupportedOperationException("VR mode not supported on this device!"); } synchronized (this) { + if (tid > 0 && mVrState == PERSISTENT_VR_MODE) { + Slog.e(TAG, "VR thread cannot be set in persistent VR mode!"); + return; + } ProcessRecord proc; synchronized (mPidsSelfLocked) { final int pid = Binder.getCallingPid(); proc = mPidsSelfLocked.get(pid); + if (proc != null && mVrState == VR_MODE && tid >= 0) { + proc.vrThreadTid = updateVrThreadLocked(proc, proc.vrThreadTid, pid, tid); + mTopAppVrThreadTid = proc.vrThreadTid; + } + } + } + } - if (proc != null && mInVrMode && tid >= 0) { - // ensure the tid belongs to the process - if (!Process.isThreadInProcess(pid, tid)) { - throw new IllegalArgumentException("VR thread does not belong to process"); - } + @Override + public void setPersistentVrThread(int tid) { + if (checkCallingPermission(permission.RESTRICTED_VR_ACCESS) != PERMISSION_GRANTED) { + String msg = "Permission Denial: setPersistentVrThread() from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid() + + " requires " + permission.RESTRICTED_VR_ACCESS; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } + if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) { + throw new UnsupportedOperationException("VR mode not supported on this device!"); + } - // reset existing VR thread to CFS if this thread still exists and belongs to - // the calling process - if (proc.vrThreadTid != 0 - && Process.isThreadInProcess(pid, proc.vrThreadTid)) { - try { - Process.setThreadScheduler(proc.vrThreadTid, Process.SCHED_OTHER, 0); - } catch (IllegalArgumentException e) { - // Ignore this. Only occurs in race condition where previous VR thread - // was destroyed during this method call. - } - } + synchronized (this) { + // Disable any existing VR thread. + if (mTopAppVrThreadTid > 0) { + Process.setThreadScheduler(mTopAppVrThreadTid, Process.SCHED_OTHER, 0); + mTopAppVrThreadTid = 0; + } - proc.vrThreadTid = tid; + if (tid > 0 && mVrState != PERSISTENT_VR_MODE) { + Slog.e(TAG, "Persistent VR thread may only be set in persistent VR mode!"); + return; + } + ProcessRecord proc; + synchronized (mPidsSelfLocked) { + final int pid = Binder.getCallingPid(); + mPersistentVrThreadTid = + updateVrThreadLocked(null, mPersistentVrThreadTid, pid, tid); + } + } + } - // promote to FIFO now if the tid is non-zero - try { - if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP && - proc.vrThreadTid > 0) { - Process.setThreadScheduler(proc.vrThreadTid, - Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1); - } - } catch (IllegalArgumentException e) { - Slog.e(TAG, "Failed to set scheduling policy, thread does" - + " not exist:\n" + e); - } - } + /** + * Used by setVrThread and setPersistentVrThread to update a thread's priority. When proc is + * non-null it must be in SCHED_GROUP_TOP_APP. When it is null, the tid is unconditionally + * rescheduled. + */ + private int updateVrThreadLocked(ProcessRecord proc, int lastTid, int pid, int tid) { + // ensure the tid belongs to the process + if (!Process.isThreadInProcess(pid, tid)) { + throw new IllegalArgumentException("VR thread does not belong to process"); + } + + // reset existing VR thread to CFS if this thread still exists and belongs to + // the calling process + if (lastTid != 0 && Process.isThreadInProcess(pid, lastTid)) { + try { + Process.setThreadScheduler(lastTid, Process.SCHED_OTHER, 0); + } catch (IllegalArgumentException e) { + // Ignore this. Only occurs in race condition where previous VR thread + // was destroyed during this method call. } } + + // promote to FIFO now if the tid is non-zero + try { + if ((proc == null || proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) + && tid > 0) { + Process.setThreadScheduler(tid, + Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1); + } + return tid; + } catch (IllegalArgumentException e) { + Slog.e(TAG, "Failed to set scheduling policy, thread does" + + " not exist:\n" + e); + } + return lastTid; } @Override @@ -13643,7 +13762,10 @@ public class ActivityManagerService extends IActivityManager.Stub mLocalDeviceIdleController = LocalServices.getService(DeviceIdleController.LocalService.class); mAssistUtils = new AssistUtils(mContext); - + VrManagerInternal vrManagerInternal = LocalServices.getService(VrManagerInternal.class); + if (vrManagerInternal != null) { + vrManagerInternal.addPersistentVrModeStateListener(mPersistentVrModeListener); + } // Make sure we have the current profile info, since it is needed for security checks. mUserController.onSystemReady(); mRecentTasks.onSystemReadyLocked(); @@ -19740,7 +19862,7 @@ public class ActivityManagerService extends IActivityManager.Stub mUserController.getCurrentUserIdLocked()); // TODO: If our config changes, should we auto dismiss any currently showing dialogs? - mShowDialogs = shouldShowDialogs(mTempConfig, mInVrMode); + mShowDialogs = shouldShowDialogs(mTempConfig, mVrState != NON_VR_MODE); AttributeCache ac = AttributeCache.instance(); if (ac != null) { @@ -19777,14 +19899,16 @@ public class ActivityManagerService extends IActivityManager.Stub Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_REPLACE_PENDING - | Intent.FLAG_RECEIVER_FOREGROUND); + | Intent.FLAG_RECEIVER_FOREGROUND + | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL); if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) { intent = new Intent(Intent.ACTION_LOCALE_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND - | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); + | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND + | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); if (initLocale || !mProcessesReady) { intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); } @@ -21279,10 +21403,11 @@ public class ActivityManagerService extends IActivityManager.Stub // do nothing if we already switched to RT if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) { // Switch VR thread for app to SCHED_FIFO - if (mInVrMode && app.vrThreadTid != 0) { + if (mVrState == VR_MODE && app.vrThreadTid != 0) { try { Process.setThreadScheduler(app.vrThreadTid, Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1); + mTopAppVrThreadTid = app.vrThreadTid; } catch (IllegalArgumentException e) { // thread died, ignore } @@ -21330,6 +21455,7 @@ public class ActivityManagerService extends IActivityManager.Stub // Safe to do even if we're not in VR mode if (app.vrThreadTid != 0) { Process.setThreadScheduler(app.vrThreadTid, Process.SCHED_OTHER, 0); + mTopAppVrThreadTid = 0; } if (mUseFifoUiScheduling) { // Reset UI pipeline to SCHED_OTHER diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 2b2471b28aa5..7868fdfd864a 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -26,7 +26,7 @@ import static android.app.ActivityManager.StackId.HOME_STACK_ID; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; import static android.app.AppOpsManager.MODE_ALLOWED; -import static android.app.AppOpsManager.OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE; +import static android.app.AppOpsManager.OP_PICTURE_IN_PICTURE; import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION; import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT; @@ -1005,6 +1005,11 @@ final class ActivityRecord implements AppWindowContainerListener { * the activity is not currently visible and {@param noThrow} is not set. */ boolean checkEnterPictureInPictureState(String caller, boolean noThrow) { + // Check app-ops and see if PiP is supported for this package + if (!checkEnterPictureInPictureAppOpsState()) { + return false; + } + boolean isCurrentAppLocked = mStackSupervisor.getLockTaskModeState() != LOCK_TASK_MODE_NONE; boolean isKeyguardLocked = service.isKeyguardLocked(); boolean hasPinnedStack = mStackSupervisor.getStack(PINNED_STACK_ID) != null; @@ -1022,15 +1027,13 @@ final class ActivityRecord implements AppWindowContainerListener { // require that there is not an existing PiP activity and that the current system // state supports entering PiP return isNotLockedOrOnKeyguard && !hasPinnedStack - && supportsPictureInPictureWhilePausing - && checkEnterPictureInPictureOnHideAppOpsState(); + && supportsPictureInPictureWhilePausing; case STOPPING: // When stopping in a valid state, then only allow enter PiP as in the pause state. // Otherwise, fall through to throw an exception if the caller is trying to enter // PiP in an invalid stopping state. if (supportsPictureInPictureWhilePausing) { - return isNotLockedOrOnKeyguard && !hasPinnedStack - && checkEnterPictureInPictureOnHideAppOpsState(); + return isNotLockedOrOnKeyguard && !hasPinnedStack; } default: if (noThrow) { @@ -1044,11 +1047,11 @@ final class ActivityRecord implements AppWindowContainerListener { } /** - * @return Whether AppOps allows this package to enter picture-in-picture when it is hidden. + * @return Whether AppOps allows this package to enter picture-in-picture. */ - private boolean checkEnterPictureInPictureOnHideAppOpsState() { + private boolean checkEnterPictureInPictureAppOpsState() { try { - return service.getAppOpsService().checkOperation(OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE, + return service.getAppOpsService().checkOperation(OP_PICTURE_IN_PICTURE, appInfo.uid, packageName) == MODE_ALLOWED; } catch (RemoteException e) { // Local call diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 2885e663af3f..e64b4b325642 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -4983,6 +4983,11 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } task.setStack(null); + + // Notify if a task from the pinned stack is being removed (or moved depending on the mode) + if (mStackId == PINNED_STACK_ID) { + mService.mTaskChangeNotificationController.notifyActivityUnpinned(); + } } TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent, diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 217515b936dd..c1bff3692938 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -399,6 +399,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D /** Mapping from (ActivityStack/TaskStack).mStackId to their current state */ SparseArray<ActivityContainer> mActivityContainers = new SparseArray<>(); + // TODO: There should be an ActivityDisplayController coordinating am/wm interaction. /** Mapping from displayId to display current state */ private final SparseArray<ActivityDisplay> mActivityDisplays = new SparseArray<>(); @@ -2853,7 +2854,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D resumeFocusedStackTopActivityLocked(); stack.animateResizePinnedStack(bounds, -1 /* animationDuration */); - mService.mTaskChangeNotificationController.notifyActivityPinned(); + mService.mTaskChangeNotificationController.notifyActivityPinned(r.packageName); } /** Move activity with its stack to front and make the stack focused. */ @@ -2885,9 +2886,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return true; } - ActivityRecord findTaskLocked(ActivityRecord r) { + ActivityRecord findTaskLocked(ActivityRecord r, int displayId) { mTmpFindTaskResult.r = null; mTmpFindTaskResult.matchedByRootAffinity = false; + ActivityRecord affinityMatch = null; if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r); for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks; @@ -2903,17 +2905,22 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D continue; } stack.findTaskLocked(r, mTmpFindTaskResult); - // It is possible to have task in multiple stacks with the same root affinity. - // If the match we found was based on root affinity we keep on looking to see if - // there is a better match in another stack. We eventually return the match based - // on root affinity if we don't find a better match. - if (mTmpFindTaskResult.r != null && !mTmpFindTaskResult.matchedByRootAffinity) { - return mTmpFindTaskResult.r; + // It is possible to have tasks in multiple stacks with the same root affinity, so + // we should keep looking after finding an affinity match to see if there is a + // better match in another stack. Also, task affinity isn't a good enough reason + // to target a display which isn't the source of the intent, so skip any affinity + // matches not on the specified display. + if (mTmpFindTaskResult.r != null) { + if (!mTmpFindTaskResult.matchedByRootAffinity) { + return mTmpFindTaskResult.r; + } else if (mTmpFindTaskResult.r.getDisplayId() == displayId) { + affinityMatch = mTmpFindTaskResult.r; + } } } } - if (DEBUG_TASKS && mTmpFindTaskResult.r == null) Slog.d(TAG_TASKS, "No task found"); - return mTmpFindTaskResult.r; + if (DEBUG_TASKS && affinityMatch == null) Slog.d(TAG_TASKS, "No task found"); + return affinityMatch; } ActivityRecord findActivityLocked(Intent intent, ActivityInfo info, @@ -3719,11 +3726,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } mActivityDisplays.put(displayId, activityDisplay); calculateDefaultMinimalSizeOfResizeableTasks(activityDisplay); + mWindowManager.onDisplayAdded(displayId); } } - if (newDisplay) { - mWindowManager.onDisplayAdded(displayId); - } } /** Check if display with specified id is added to the list. */ @@ -3762,9 +3767,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } mActivityDisplays.remove(displayId); + mWindowManager.onDisplayRemoved(displayId); } } - mWindowManager.onDisplayRemoved(displayId); } private void handleDisplayChanged(int displayId) { @@ -3773,8 +3778,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D if (activityDisplay != null) { // TODO: Update the bounds. } + mWindowManager.onDisplayChanged(displayId); } - mWindowManager.onDisplayChanged(displayId); } private StackInfo getStackInfoLocked(ActivityStack stack) { @@ -4734,7 +4739,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D init(mVirtualDisplay.getDisplay()); - mWindowManager.handleDisplayAdded(mDisplayId); + mWindowManager.onDisplayAdded(mDisplayId); } void setSurface(Surface surface) { diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index 2bbfc21c299f..1b7b22527df1 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -54,6 +54,7 @@ import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP; +import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS; @@ -169,6 +170,7 @@ class ActivityStarter { private boolean mDoResume; private int mStartFlags; private ActivityRecord mSourceRecord; + private int mSourceDisplayId; private TaskRecord mInTask; private boolean mAddingToTask; @@ -208,6 +210,7 @@ class ActivityStarter { mDoResume = false; mStartFlags = 0; mSourceRecord = null; + mSourceDisplayId = INVALID_DISPLAY; mInTask = null; mAddingToTask = false; @@ -451,8 +454,8 @@ class ActivityStarter { Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true, true, false) + "} from uid " + callingUid + " on display " + (container == null ? (mSupervisor.mFocusedStack == null ? - Display.DEFAULT_DISPLAY : mSupervisor.mFocusedStack.mDisplayId) : - (container.mActivityDisplay == null ? Display.DEFAULT_DISPLAY : + DEFAULT_DISPLAY : mSupervisor.mFocusedStack.mDisplayId) : + (container.mActivityDisplay == null ? DEFAULT_DISPLAY : container.mActivityDisplay.mDisplayId))); } } @@ -1193,6 +1196,11 @@ class ActivityStarter { mVoiceSession = voiceSession; mVoiceInteractor = voiceInteractor; + mSourceDisplayId = sourceRecord != null ? sourceRecord.getDisplayId() : INVALID_DISPLAY; + if (mSourceDisplayId == INVALID_DISPLAY) { + mSourceDisplayId = DEFAULT_DISPLAY; + } + mLaunchBounds = getOverrideBounds(r, options, inTask); mLaunchSingleTop = r.launchMode == LAUNCH_SINGLE_TOP; @@ -1439,7 +1447,7 @@ class ActivityStarter { !mLaunchSingleTask); } else { // Otherwise find the best task to put the activity in. - intentActivity = mSupervisor.findTaskLocked(mStartActivity); + intentActivity = mSupervisor.findTaskLocked(mStartActivity, mSourceDisplayId); } } return intentActivity; @@ -1925,33 +1933,31 @@ class ActivityStarter { return container.mStack; } - // The fullscreen stack can contain any task regardless of if the task is resizeable - // or not. So, we let the task go in the fullscreen task if it is the focus stack. - // Same also applies to dynamic stacks, as they behave similar to fullscreen stack. - // If the freeform or docked stack has focus, and the activity to be launched is resizeable, - // we can also put it in the focused stack. if (canLaunchIntoFocusedStack(r, newTask)) { if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: Have a focused stack=" + mSupervisor.mFocusedStack); return mSupervisor.mFocusedStack; } - // We first try to put the task in the first dynamic stack on home display. - final ArrayList<ActivityStack> homeDisplayStacks = mSupervisor.mHomeStack.mStacks; - for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) { - stack = homeDisplayStacks.get(stackNdx); - if (isDynamicStack(stack.mStackId)) { - if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, - "computeStackFocus: Setting focused stack=" + stack); - return stack; + if (mSourceDisplayId == DEFAULT_DISPLAY) { + // We first try to put the task in the first dynamic stack on home display. + final ArrayList<ActivityStack> homeDisplayStacks = mSupervisor.mHomeStack.mStacks; + for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) { + stack = homeDisplayStacks.get(stackNdx); + if (isDynamicStack(stack.mStackId)) { + if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, + "computeStackFocus: Setting focused stack=" + stack); + return stack; + } } + // If there is no suitable dynamic stack then we figure out which static stack to use. + final int stackId = task != null ? task.getLaunchStackId() : + bounds != null ? FREEFORM_WORKSPACE_STACK_ID : + FULLSCREEN_WORKSPACE_STACK_ID; + stack = mSupervisor.getStack(stackId, CREATE_IF_NEEDED, ON_TOP); + } else { + stack = mSupervisor.getValidLaunchStackOnDisplay(mSourceDisplayId, r); } - - // If there is no suitable dynamic stack then we figure out which static stack to use. - final int stackId = task != null ? task.getLaunchStackId() : - bounds != null ? FREEFORM_WORKSPACE_STACK_ID : - FULLSCREEN_WORKSPACE_STACK_ID; - stack = mSupervisor.getStack(stackId, CREATE_IF_NEEDED, ON_TOP); if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r=" + r + " stackId=" + stack.mStackId); return stack; @@ -1959,35 +1965,35 @@ class ActivityStarter { /** Check if provided activity record can launch in currently focused stack. */ private boolean canLaunchIntoFocusedStack(ActivityRecord r, boolean newTask) { - // The fullscreen stack can contain any task regardless of if the task is resizeable - // or not. So, we let the task go in the fullscreen task if it is the focus stack. - // Same also applies to dynamic stacks, as they behave similar to fullscreen stack. - // If the freeform or docked stack has focus, and the activity to be launched is resizeable, - // we can also put it in the focused stack. final ActivityStack focusedStack = mSupervisor.mFocusedStack; final int focusedStackId = mSupervisor.mFocusedStack.mStackId; final boolean canUseFocusedStack; switch (focusedStackId) { case FULLSCREEN_WORKSPACE_STACK_ID: + // The fullscreen stack can contain any task regardless of if the task is resizeable + // or not. So, we let the task go in the fullscreen task if it is the focus stack. canUseFocusedStack = true; break; case ASSISTANT_STACK_ID: canUseFocusedStack = r.isAssistantActivity(); break; case DOCKED_STACK_ID: + // Any activty which supports split screen can go in the docked stack. canUseFocusedStack = r.supportsSplitScreen(); break; case FREEFORM_WORKSPACE_STACK_ID: + // Any activty which supports freeform can go in the freeform stack. canUseFocusedStack = r.supportsFreeform(); break; default: - canUseFocusedStack = isDynamicStack(focusedStackId) - && mSupervisor.isCallerAllowedToLaunchOnDisplay(r.launchedFromPid, - r.launchedFromUid, focusedStack.mDisplayId); + // Dynamic stacks behave similarly to the fullscreen stack and can contain any task. + canUseFocusedStack = isDynamicStack(focusedStackId); } return canUseFocusedStack - && (!newTask || focusedStack.mActivityContainer.isEligibleForNewTasks()); + && (!newTask || focusedStack.mActivityContainer.isEligibleForNewTasks()) + // We strongly prefer to launch activities on the same display as their source. + && (mSourceDisplayId == focusedStack.mDisplayId); } private ActivityStack getLaunchStack(ActivityRecord r, int launchFlags, TaskRecord task, @@ -2034,7 +2040,8 @@ class ActivityStarter { return mSupervisor.getValidLaunchStackOnDisplay(launchDisplayId, r); } - if ((launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) == 0) { + if ((launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) == 0 + || mSourceDisplayId != DEFAULT_DISPLAY) { return null; } // Otherwise handle adjacent launch. diff --git a/services/core/java/com/android/server/am/TaskChangeNotificationController.java b/services/core/java/com/android/server/am/TaskChangeNotificationController.java index 3cec7e478046..94cf092baed3 100644 --- a/services/core/java/com/android/server/am/TaskChangeNotificationController.java +++ b/services/core/java/com/android/server/am/TaskChangeNotificationController.java @@ -47,6 +47,7 @@ class TaskChangeNotificationController { static final int NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG = 14; static final int NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG = 15; static final int NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG = 16; + static final int NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG = 17; // Delay in notifying task stack change listeners (in millis) static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100; @@ -94,7 +95,11 @@ class TaskChangeNotificationController { }; private final TaskStackConsumer mNotifyActivityPinned = (l, m) -> { - l.onActivityPinned(); + l.onActivityPinned((String) m.obj); + }; + + private final TaskStackConsumer mNotifyActivityUnpinned = (l, m) -> { + l.onActivityUnpinned(); }; private final TaskStackConsumer mNotifyPinnedActivityRestartAttempt = (l, m) -> { @@ -168,6 +173,9 @@ class TaskChangeNotificationController { case NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG: forAllRemoteListeners(mNotifyActivityPinned, msg); break; + case NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG: + forAllRemoteListeners(mNotifyActivityUnpinned, msg); + break; case NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG: forAllRemoteListeners(mNotifyPinnedActivityRestartAttempt, msg); break; @@ -263,13 +271,22 @@ class TaskChangeNotificationController { } /** Notifies all listeners when an Activity is pinned. */ - void notifyActivityPinned() { + void notifyActivityPinned(String packageName) { mHandler.removeMessages(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG); - final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG); + final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG, + packageName); forAllLocalListeners(mNotifyActivityPinned, msg); msg.sendToTarget(); } + /** Notifies all listeners when an Activity is unpinned. */ + void notifyActivityUnpinned() { + mHandler.removeMessages(NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG); + final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG); + forAllLocalListeners(mNotifyActivityUnpinned, msg); + msg.sendToTarget(); + } + /** * Notifies all listeners when an attempt was made to start an an activity that is already * running in the pinned stack and the activity was not actually started, but the task is diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java index 81e891a90499..1eea0a932e0c 100644 --- a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java +++ b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java @@ -16,6 +16,17 @@ package com.android.server.connectivity; +import static android.net.NetworkCapabilities.MAX_TRANSPORT; +import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; +import static android.net.NetworkCapabilities.TRANSPORT_VPN; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE; +import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent; +import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityLog; +import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.NetworkId; + import android.net.ConnectivityMetricsEvent; import android.net.metrics.ApfProgramEvent; import android.net.metrics.ApfStats; @@ -29,14 +40,12 @@ import android.net.metrics.NetworkEvent; import android.net.metrics.RaEvent; import android.net.metrics.ValidationProbeEvent; import android.os.Parcelable; +import android.util.SparseArray; import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass; import java.io.IOException; import java.util.ArrayList; import java.util.List; -import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent; -import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityLog; -import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.NetworkId; /** {@hide} */ final public class IpConnectivityEventBuilder { @@ -73,6 +82,12 @@ final public class IpConnectivityEventBuilder { return null; } out.timeMs = ev.timestamp; + out.networkId = ev.netId; + out.transports = ev.transports; + if (ev.ifname != null) { + out.ifName = ev.ifname; + } + inferLinkLayer(out); return out; } @@ -137,14 +152,12 @@ final public class IpConnectivityEventBuilder { private static void setDhcpErrorEvent(IpConnectivityEvent out, DhcpErrorEvent in) { IpConnectivityLogClass.DHCPEvent dhcpEvent = new IpConnectivityLogClass.DHCPEvent(); - dhcpEvent.ifName = in.ifName; dhcpEvent.setErrorCode(in.errorCode); out.setDhcpEvent(dhcpEvent); } private static void setDhcpClientEvent(IpConnectivityEvent out, DhcpClientEvent in) { IpConnectivityLogClass.DHCPEvent dhcpEvent = new IpConnectivityLogClass.DHCPEvent(); - dhcpEvent.ifName = in.ifName; dhcpEvent.setStateTransition(in.msg); dhcpEvent.durationMs = in.durationMs; out.setDhcpEvent(dhcpEvent); @@ -163,7 +176,6 @@ final public class IpConnectivityEventBuilder { private static void setIpManagerEvent(IpConnectivityEvent out, IpManagerEvent in) { IpConnectivityLogClass.IpProvisioningEvent ipProvisioningEvent = new IpConnectivityLogClass.IpProvisioningEvent(); - ipProvisioningEvent.ifName = in.ifName; ipProvisioningEvent.eventType = in.eventType; ipProvisioningEvent.latencyMs = (int) in.durationMs; out.setIpProvisioningEvent(ipProvisioningEvent); @@ -172,7 +184,6 @@ final public class IpConnectivityEventBuilder { private static void setIpReachabilityEvent(IpConnectivityEvent out, IpReachabilityEvent in) { IpConnectivityLogClass.IpReachabilityEvent ipReachabilityEvent = new IpConnectivityLogClass.IpReachabilityEvent(); - ipReachabilityEvent.ifName = in.ifName; ipReachabilityEvent.eventType = in.eventType; out.setIpReachabilityEvent(ipReachabilityEvent); } @@ -280,4 +291,70 @@ final public class IpConnectivityEventBuilder { private static boolean isBitSet(int flags, int bit) { return (flags & (1 << bit)) != 0; } + + private static void inferLinkLayer(IpConnectivityEvent ev) { + int linkLayer = IpConnectivityLogClass.UNKNOWN; + if (ev.transports != 0) { + linkLayer = transportsToLinkLayer(ev.transports); + } else if (ev.ifName != null) { + linkLayer = ifnameToLinkLayer(ev.ifName); + } + if (linkLayer == IpConnectivityLogClass.UNKNOWN) { + return; + } + ev.linkLayer = linkLayer; + ev.ifName = ""; + } + + private static int transportsToLinkLayer(long transports) { + switch (Long.bitCount(transports)) { + case 0: + return IpConnectivityLogClass.UNKNOWN; + case 1: + int t = Long.numberOfTrailingZeros(transports); + return transportToLinkLayer(t); + default: + return IpConnectivityLogClass.MULTIPLE; + } + } + + private static int transportToLinkLayer(int transport) { + if (0 <= transport && transport < TRANSPORT_LINKLAYER_MAP.length) { + return TRANSPORT_LINKLAYER_MAP[transport]; + } + return IpConnectivityLogClass.UNKNOWN; + } + + private static final int[] TRANSPORT_LINKLAYER_MAP = new int[MAX_TRANSPORT + 1]; + static { + TRANSPORT_LINKLAYER_MAP[TRANSPORT_CELLULAR] = IpConnectivityLogClass.CELLULAR; + TRANSPORT_LINKLAYER_MAP[TRANSPORT_WIFI] = IpConnectivityLogClass.WIFI; + TRANSPORT_LINKLAYER_MAP[TRANSPORT_BLUETOOTH] = IpConnectivityLogClass.BLUETOOTH; + TRANSPORT_LINKLAYER_MAP[TRANSPORT_ETHERNET] = IpConnectivityLogClass.ETHERNET; + TRANSPORT_LINKLAYER_MAP[TRANSPORT_VPN] = IpConnectivityLogClass.UNKNOWN; + // TODO: change mapping TRANSPORT_WIFI_AWARE -> WIFI_AWARE + TRANSPORT_LINKLAYER_MAP[TRANSPORT_WIFI_AWARE] = IpConnectivityLogClass.UNKNOWN; + }; + + private static int ifnameToLinkLayer(String ifname) { + // Do not try to catch all interface names with regexes, instead only catch patterns that + // are cheap to check, and otherwise fallback on postprocessing in aggregation layer. + for (int i = 0; i < IFNAME_LINKLAYER_MAP.size(); i++) { + String pattern = IFNAME_LINKLAYER_MAP.valueAt(i); + if (ifname.startsWith(pattern)) { + return IFNAME_LINKLAYER_MAP.keyAt(i); + } + } + return IpConnectivityLogClass.UNKNOWN; + } + + private static final SparseArray<String> IFNAME_LINKLAYER_MAP = new SparseArray<String>(); + static { + IFNAME_LINKLAYER_MAP.put(IpConnectivityLogClass.CELLULAR, "rmnet"); + IFNAME_LINKLAYER_MAP.put(IpConnectivityLogClass.WIFI, "wlan"); + IFNAME_LINKLAYER_MAP.put(IpConnectivityLogClass.BLUETOOTH, "bt-pan"); + // TODO: rekey to USB + IFNAME_LINKLAYER_MAP.put(IpConnectivityLogClass.ETHERNET, "usb"); + // TODO: add mappings for nan -> WIFI_AWARE and p2p -> WIFI_P2P + } } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 9d93cc754007..381f37028e7a 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -2524,10 +2524,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // adjust stats accounting based on foreground status private void updateNetworkStats(int uid, boolean uidForeground) { + if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) { + Trace.traceBegin(Trace.TRACE_TAG_NETWORK, + "updateNetworkStats: " + uid + "/" + (uidForeground ? "F" : "B")); + } try { mNetworkStats.setUidForeground(uid, uidForeground); } catch (RemoteException e) { // ignored; service lives in system_server + } finally { + Trace.traceEnd(Trace.TRACE_TAG_NETWORK); } } @@ -2655,12 +2661,19 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { void updateRuleForAppIdleUL(int uid) { if (!isUidValidForBlacklistRules(uid)) return; - int appId = UserHandle.getAppId(uid); - if (!mPowerSaveTempWhitelistAppIds.get(appId) && isUidIdle(uid) - && !isUidForegroundOnRestrictPowerUL(uid)) { - setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DENY); - } else { - setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DEFAULT); + if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) { + Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRuleForAppIdleUL: " + uid ); + } + try { + int appId = UserHandle.getAppId(uid); + if (!mPowerSaveTempWhitelistAppIds.get(appId) && isUidIdle(uid) + && !isUidForegroundOnRestrictPowerUL(uid)) { + setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DENY); + } else { + setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DEFAULT); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_NETWORK); } } @@ -2696,7 +2709,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * {@link #mRestrictPower}, or {@link #mDeviceIdleMode} value. */ private void updateRulesForGlobalChangeAL(boolean restrictedNetworksChanged) { - Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForGlobalChangeAL"); + if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) { + Trace.traceBegin(Trace.TRACE_TAG_NETWORK, + "updateRulesForGlobalChangeAL: " + (restrictedNetworksChanged ? "R" : "-")); + } try { updateRulesForAppIdleUL(); updateRulesForRestrictPowerUL(); @@ -2749,14 +2765,27 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForRestrictPowerUL-" + type); } try { + // update rules for all installed applications + final PackageManager pm = mContext.getPackageManager(); + final List<UserInfo> users; + final List<ApplicationInfo> apps; - // update rules for all installed applications - final List<UserInfo> users = mUserManager.getUsers(); - final List<ApplicationInfo> apps = pm.getInstalledApplications( - PackageManager.MATCH_ANY_USER | PackageManager.MATCH_DISABLED_COMPONENTS - | PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); + Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "list-users"); + try { + users = mUserManager.getUsers(); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_NETWORK); + } + Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "list-uids"); + try { + apps = pm.getInstalledApplications( + PackageManager.MATCH_ANY_USER | PackageManager.MATCH_DISABLED_COMPONENTS + | PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_NETWORK); + } final int usersSize = users.size(); final int appsSize = apps.size(); @@ -2778,9 +2807,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } } finally { - if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) { - Trace.traceEnd(Trace.TRACE_TAG_NETWORK); - } + Trace.traceEnd(Trace.TRACE_TAG_NETWORK); } } @@ -2931,6 +2958,18 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * */ private void updateRulesForDataUsageRestrictionsUL(int uid) { + if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) { + Trace.traceBegin(Trace.TRACE_TAG_NETWORK, + "updateRulesForDataUsageRestrictionsUL: " + uid); + } + try { + updateRulesForDataUsageRestrictionsULInner(uid); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_NETWORK); + } + } + + private void updateRulesForDataUsageRestrictionsULInner(int uid) { if (!isUidValidForWhitelistRules(uid)) { if (LOGD) Slog.d(TAG, "no need to update restrict data rules for uid " + uid); return; @@ -3073,6 +3112,19 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * @return the new computed rules for the uid */ private int updateRulesForPowerRestrictionsUL(int uid, int oldUidRules, boolean paroled) { + if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) { + Trace.traceBegin(Trace.TRACE_TAG_NETWORK, + "updateRulesForPowerRestrictionsUL: " + uid + "/" + oldUidRules + "/" + + (paroled ? "P" : "-")); + } + try { + return updateRulesForPowerRestrictionsULInner(uid, oldUidRules, paroled); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_NETWORK); + } + } + + private int updateRulesForPowerRestrictionsULInner(int uid, int oldUidRules, boolean paroled) { if (!isUidValidForBlacklistRules(uid)) { if (LOGD) Slog.d(TAG, "no need to update restrict power rules for uid " + uid); return RULE_NONE; @@ -3432,20 +3484,28 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * Add or remove a uid to the firewall blacklist for all network ifaces. */ private void setUidFirewallRule(int chain, int uid, int rule) { - if (chain == FIREWALL_CHAIN_DOZABLE) { - mUidFirewallDozableRules.put(uid, rule); - } else if (chain == FIREWALL_CHAIN_STANDBY) { - mUidFirewallStandbyRules.put(uid, rule); - } else if (chain == FIREWALL_CHAIN_POWERSAVE) { - mUidFirewallPowerSaveRules.put(uid, rule); + if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) { + Trace.traceBegin(Trace.TRACE_TAG_NETWORK, + "setUidFirewallRule: " + chain + "/" + uid + "/" + rule); } - try { - mNetworkManager.setFirewallUidRule(chain, uid, rule); - } catch (IllegalStateException e) { - Log.wtf(TAG, "problem setting firewall uid rules", e); - } catch (RemoteException e) { - // ignored; service lives in system_server + if (chain == FIREWALL_CHAIN_DOZABLE) { + mUidFirewallDozableRules.put(uid, rule); + } else if (chain == FIREWALL_CHAIN_STANDBY) { + mUidFirewallStandbyRules.put(uid, rule); + } else if (chain == FIREWALL_CHAIN_POWERSAVE) { + mUidFirewallPowerSaveRules.put(uid, rule); + } + + try { + mNetworkManager.setFirewallUidRule(chain, uid, rule); + } catch (IllegalStateException e) { + Log.wtf(TAG, "problem setting firewall uid rules", e); + } catch (RemoteException e) { + // ignored; service lives in system_server + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_NETWORK); } } diff --git a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java index 3432ecd19efe..b0730efe635c 100644 --- a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java +++ b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java @@ -68,11 +68,12 @@ final class EphemeralResolverConnection { mIntent = new Intent(Intent.ACTION_RESOLVE_EPHEMERAL_PACKAGE).setComponent(componentName); } - public final List<InstantAppResolveInfo> getInstantAppResolveInfoList(int hashPrefix[]) { + public final List<InstantAppResolveInfo> getInstantAppResolveInfoList(int hashPrefix[], + String token) { throwIfCalledOnMainThread(); try { return mGetEphemeralResolveInfoCaller.getEphemeralResolveInfoList( - getRemoteInstanceLazy(), hashPrefix); + getRemoteInstanceLazy(), hashPrefix, token); } catch (RemoteException re) { } catch (TimeoutException te) { } finally { @@ -83,8 +84,9 @@ final class EphemeralResolverConnection { return null; } - public final void getInstantAppIntentFilterList(int hashPrefix[], String hostName, - PhaseTwoCallback callback, Handler callbackHandler, final long startTime) { + public final void getInstantAppIntentFilterList(int hashPrefix[], String token, + String hostName, PhaseTwoCallback callback, Handler callbackHandler, + final long startTime) { final IRemoteCallback remoteCallback = new IRemoteCallback.Stub() { @Override public void sendResult(Bundle data) throws RemoteException { @@ -100,9 +102,8 @@ final class EphemeralResolverConnection { } }; try { - // TODO deprecate sequence; it's never used - getRemoteInstanceLazy().getInstantAppIntentFilterList( - hashPrefix, 0 /*sequence*/, hostName, remoteCallback); + getRemoteInstanceLazy() + .getInstantAppIntentFilterList(hashPrefix, token, hostName, remoteCallback); } catch (RemoteException re) { } catch (TimeoutException te) { } @@ -215,10 +216,10 @@ final class EphemeralResolverConnection { } public List<InstantAppResolveInfo> getEphemeralResolveInfoList( - IInstantAppResolver target, int hashPrefix[]) + IInstantAppResolver target, int hashPrefix[], String token) throws RemoteException, TimeoutException { final int sequence = onBeforeRemoteCall(); - target.getInstantAppResolveInfoList(hashPrefix, sequence, mCallback); + target.getInstantAppResolveInfoList(hashPrefix, token, sequence, mCallback); return getResultTimed(sequence); } } diff --git a/services/core/java/com/android/server/pm/InstantAppResolver.java b/services/core/java/com/android/server/pm/InstantAppResolver.java index 3396954e6ddc..86124a823810 100644 --- a/services/core/java/com/android/server/pm/InstantAppResolver.java +++ b/services/core/java/com/android/server/pm/InstantAppResolver.java @@ -40,6 +40,7 @@ import android.content.pm.InstantAppResolveInfo; import android.content.pm.InstantAppResolveInfo.InstantAppDigest; import android.metrics.LogMaker; import android.os.Binder; +import android.os.Build; import android.os.Handler; import android.os.RemoteException; import android.util.Log; @@ -57,6 +58,9 @@ import java.util.UUID; /** @hide */ public abstract class InstantAppResolver { + private static final boolean DEBUG_EPHEMERAL = Build.IS_DEBUGGABLE; + private static final String TAG = "PackageManager"; + private static int RESOLUTION_SUCCESS = 0; private static int RESOLUTION_FAILURE = 1; @@ -70,6 +74,9 @@ public abstract class InstantAppResolver { public static AuxiliaryResolveInfo doInstantAppResolutionPhaseOne(Context context, EphemeralResolverConnection connection, InstantAppRequest requestObj) { + if (DEBUG_EPHEMERAL) { + Log.d(TAG, "Resolving phase 1"); + } final long startTime = System.currentTimeMillis(); final String token = UUID.randomUUID().toString(); final Intent intent = requestObj.origIntent; @@ -77,11 +84,14 @@ public abstract class InstantAppResolver { new InstantAppDigest(intent.getData().getHost(), 5 /*maxDigests*/); final int[] shaPrefix = digest.getDigestPrefix(); final List<InstantAppResolveInfo> instantAppResolveInfoList = - connection.getInstantAppResolveInfoList(shaPrefix); // pass token + connection.getInstantAppResolveInfoList(shaPrefix, token); final AuxiliaryResolveInfo resolveInfo; if (instantAppResolveInfoList == null || instantAppResolveInfoList.size() == 0) { // No hash prefix match; there are no instant apps for this domain. + if (DEBUG_EPHEMERAL) { + Log.d(TAG, "No results returned"); + } resolveInfo = null; } else { resolveInfo = InstantAppResolver.filterInstantAppIntent(instantAppResolveInfoList, @@ -98,11 +108,15 @@ public abstract class InstantAppResolver { public static void doInstantAppResolutionPhaseTwo(Context context, EphemeralResolverConnection connection, InstantAppRequest requestObj, ActivityInfo instantAppInstaller, Handler callbackHandler) { + if (DEBUG_EPHEMERAL) { + Log.d(TAG, "Resolving phase 2"); + } final long startTime = System.currentTimeMillis(); final Intent intent = requestObj.origIntent; final String hostName = intent.getData().getHost(); final InstantAppDigest digest = new InstantAppDigest(hostName, 5 /*maxDigests*/); final int[] shaPrefix = digest.getDigestPrefix(); + final String token = requestObj.responseObj.token; final PhaseTwoCallback callback = new PhaseTwoCallback() { @Override @@ -111,7 +125,6 @@ public abstract class InstantAppResolver { final String packageName; final String splitName; final int versionCode; - final String token = requestObj.responseObj.token; if (instantAppResolveInfoList != null && instantAppResolveInfoList.size() > 0) { final AuxiliaryResolveInfo instantAppIntentInfo = InstantAppResolver.filterInstantAppIntent( @@ -152,7 +165,7 @@ public abstract class InstantAppResolver { } }; connection.getInstantAppIntentFilterList( - shaPrefix, hostName, callback, callbackHandler, startTime); + shaPrefix, token, hostName, callback, callbackHandler, startTime); } /** @@ -245,6 +258,9 @@ public abstract class InstantAppResolver { instantAppInfo.getIntentFilters(); // No filters; we need to start phase two if (instantAppFilters == null || instantAppFilters.isEmpty()) { + if (DEBUG_EPHEMERAL) { + Log.d(TAG, "No app filters; go to phase 2"); + } return new AuxiliaryResolveInfo(instantAppInfo, new IntentFilter(Intent.ACTION_VIEW) /*intentFilter*/, null /*splitName*/, token, true /*needsPhase2*/); @@ -269,6 +285,13 @@ public abstract class InstantAppResolver { List<AuxiliaryResolveInfo> matchedResolveInfoList = instantAppResolver.queryIntent( intent, resolvedType, false /*defaultOnly*/, userId); if (!matchedResolveInfoList.isEmpty()) { + if (DEBUG_EPHEMERAL) { + final AuxiliaryResolveInfo info = matchedResolveInfoList.get(0); + Log.d(TAG, "Found match;" + + " package: " + info.packageName + + ", split: " + info.splitName + + ", versionCode: " + info.versionCode); + } return matchedResolveInfoList.get(0); } } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 548fa1ec6d75..7eb4df83a17b 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -297,7 +297,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // These need to match the documentation/constant in // core/res/res/values/config.xml static final int LONG_PRESS_HOME_NOTHING = 0; - static final int LONG_PRESS_HOME_RECENT_SYSTEM_UI = 1; + static final int LONG_PRESS_HOME_ALL_APPS = 1; static final int LONG_PRESS_HOME_ASSIST = 2; static final int LAST_LONG_PRESS_HOME_BEHAVIOR = LONG_PRESS_HOME_ASSIST; @@ -1700,10 +1700,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { } mHomeConsumed = true; performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false); - switch (mLongPressOnHomeBehavior) { - case LONG_PRESS_HOME_RECENT_SYSTEM_UI: - toggleRecentApps(); + case LONG_PRESS_HOME_ALL_APPS: + launchAllAppsAction(); break; case LONG_PRESS_HOME_ASSIST: launchAssistAction(null, deviceId); @@ -1714,6 +1713,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + private void launchAllAppsAction() { + Intent intent = new Intent(Intent.ACTION_ALL_APPS); + startActivityAsUser(intent, UserHandle.CURRENT); + } + private void handleDoubleTapOnHome() { if (mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI) { mHomeConsumed = true; @@ -3332,8 +3336,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mHomeDoubleTapPending = false; mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable); handleDoubleTapOnHome(); - } else if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_RECENT_SYSTEM_UI - || mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI) { + } else if (mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI) { preloadRecentApps(); } } else if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) { diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java index c31369e0c282..8f114361ccff 100644 --- a/services/core/java/com/android/server/power/Notifier.java +++ b/services/core/java/com/android/server/power/Notifier.java @@ -145,10 +145,12 @@ final class Notifier { mHandler = new NotifierHandler(looper); mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON); mScreenOnIntent.addFlags( - Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); + Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND + | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); mScreenOffIntent = new Intent(Intent.ACTION_SCREEN_OFF); mScreenOffIntent.addFlags( - Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); + Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND + | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); mScreenBrightnessBoostIntent = new Intent(PowerManager.ACTION_SCREEN_BRIGHTNESS_BOOST_CHANGED); mScreenBrightnessBoostIntent.addFlags( diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 7539cd4b8cab..5edb82cfb1bb 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -4634,10 +4634,6 @@ public class WindowManagerService extends IWindowManager.Stub public static final int SHOW_STRICT_MODE_VIOLATION = 25; public static final int DO_ANIMATION_CALLBACK = 26; - public static final int DO_DISPLAY_ADDED = 27; - public static final int DO_DISPLAY_REMOVED = 28; - public static final int DO_DISPLAY_CHANGED = 29; - public static final int CLIENT_FREEZE_TIMEOUT = 30; public static final int TAP_OUTSIDE_TASK = 31; public static final int NOTIFY_ACTIVITY_DRAWN = 32; @@ -4981,22 +4977,6 @@ public class WindowManagerService extends IWindowManager.Stub break; } - case DO_DISPLAY_ADDED: - handleDisplayAdded(msg.arg1); - break; - - case DO_DISPLAY_REMOVED: - synchronized (mWindowMap) { - handleDisplayRemovedLocked(msg.arg1); - } - break; - - case DO_DISPLAY_CHANGED: - synchronized (mWindowMap) { - handleDisplayChangedLocked(msg.arg1); - } - break; - case TAP_OUTSIDE_TASK: { handleTapOutsideTask((DisplayContent)msg.obj, msg.arg1, msg.arg2); } @@ -6710,10 +6690,6 @@ public class WindowManagerService extends IWindowManager.Stub } public void onDisplayAdded(int displayId) { - mH.sendMessage(mH.obtainMessage(H.DO_DISPLAY_ADDED, displayId, 0)); - } - - public void handleDisplayAdded(int displayId) { synchronized (mWindowMap) { final Display display = mDisplayManager.getDisplay(displayId); if (display != null) { @@ -6725,28 +6701,24 @@ public class WindowManagerService extends IWindowManager.Stub } public void onDisplayRemoved(int displayId) { - mH.sendMessage(mH.obtainMessage(H.DO_DISPLAY_REMOVED, displayId, 0)); - } - - private void handleDisplayRemovedLocked(int displayId) { - final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId); - if (displayContent != null) { - displayContent.removeIfPossible(); + synchronized (mWindowMap) { + final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId); + if (displayContent != null) { + displayContent.removeIfPossible(); + } + mAnimator.removeDisplayLocked(displayId); + mWindowPlacerLocked.requestTraversal(); } - mAnimator.removeDisplayLocked(displayId); - mWindowPlacerLocked.requestTraversal(); } public void onDisplayChanged(int displayId) { - mH.sendMessage(mH.obtainMessage(H.DO_DISPLAY_CHANGED, displayId, 0)); - } - - private void handleDisplayChangedLocked(int displayId) { - final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId); - if (displayContent != null) { - displayContent.updateDisplayInfo(); + synchronized (mWindowMap) { + final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId); + if (displayContent != null) { + displayContent.updateDisplayInfo(); + } + mWindowPlacerLocked.requestTraversal(); } - mWindowPlacerLocked.requestTraversal(); } @Override diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index e6e02428ae63..27274653f760 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -619,7 +619,6 @@ public final class SystemServer { startSensorService(); traceLog.traceEnd(); }, START_SENSOR_SERVICE); - } /** @@ -647,14 +646,6 @@ public final class SystemServer { traceBeginAndSlog("StartWebViewUpdateService"); mWebViewUpdateService = mSystemServiceManager.startService(WebViewUpdateService.class); traceEnd(); - - // Start receiving calls from HIDL services. Start in in a separate thread - // because it need to connect to SensorManager. - SystemServerInitThreadPool.get().submit(() -> { - traceBeginAndSlog(START_HIDL_SERVICES); - startHidlServices(); - traceEnd(); - }, START_HIDL_SERVICES); } /** @@ -813,6 +804,15 @@ public final class SystemServer { ServiceManager.addService(Context.INPUT_SERVICE, inputManager); traceEnd(); + // Start receiving calls from HIDL services. Start in in a separate thread + // because it need to connect to SensorManager. This have to start + // after START_SENSOR_SERVICE is done. + SystemServerInitThreadPool.get().submit(() -> { + traceBeginAndSlog(START_HIDL_SERVICES); + startHidlServices(); + traceEnd(); + }, START_HIDL_SERVICES); + if (!disableVrManager) { traceBeginAndSlog("StartVrManagerService"); mSystemServiceManager.startService(VrManagerService.class); diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java index 2624f0b3a21e..ed78175bd395 100644 --- a/services/net/java/android/net/dhcp/DhcpClient.java +++ b/services/net/java/android/net/dhcp/DhcpClient.java @@ -1023,10 +1023,10 @@ public class DhcpClient extends StateMachine { } private void logError(int errorCode) { - mMetricsLog.log(new DhcpErrorEvent(mIfaceName, errorCode)); + mMetricsLog.log(mIfaceName, new DhcpErrorEvent(errorCode)); } private void logState(String name, int durationMs) { - mMetricsLog.log(new DhcpClientEvent(mIfaceName, name, durationMs)); + mMetricsLog.log(mIfaceName, new DhcpClientEvent(name, durationMs)); } } diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java index c670782b443e..59e698c39975 100644 --- a/services/net/java/android/net/ip/IpManager.java +++ b/services/net/java/android/net/ip/IpManager.java @@ -720,7 +720,7 @@ public class IpManager extends StateMachine { private void recordMetric(final int type) { if (mStartTimeMillis <= 0) { Log.wtf(mTag, "Start time undefined!"); } final long duration = SystemClock.elapsedRealtime() - mStartTimeMillis; - mMetricsLog.log(new IpManagerEvent(mInterfaceName, type, duration)); + mMetricsLog.log(mInterfaceName, new IpManagerEvent(type, duration)); } // For now: use WifiStateMachine's historical notion of provisioned. diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java index 20eac622d37f..d13449a4c8c6 100644 --- a/services/net/java/android/net/ip/IpReachabilityMonitor.java +++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java @@ -426,7 +426,7 @@ public class IpReachabilityMonitor { private void logEvent(int probeType, int errorCode) { int eventType = probeType | (errorCode & 0xff); - mMetricsLog.log(new IpReachabilityEvent(mInterfaceName, eventType)); + mMetricsLog.log(mInterfaceName, new IpReachabilityEvent(eventType)); } private void logNudFailed(ProvisioningChange delta) { @@ -434,7 +434,7 @@ public class IpReachabilityMonitor { boolean isFromProbe = (duration < getProbeWakeLockDuration()); boolean isProvisioningLost = (delta == ProvisioningChange.LOST_PROVISIONING); int eventType = IpReachabilityEvent.nudFailureEventType(isFromProbe, isProvisioningLost); - mMetricsLog.log(new IpReachabilityEvent(mInterfaceName, eventType)); + mMetricsLog.log(mInterfaceName, new IpReachabilityEvent(eventType)); } // TODO: simplify the number of objects by making this extend Thread. diff --git a/services/print/java/com/android/server/print/CompanionDeviceManagerService.java b/services/print/java/com/android/server/print/CompanionDeviceManagerService.java index e6e2cb3d99c9..9356dacc29f8 100644 --- a/services/print/java/com/android/server/print/CompanionDeviceManagerService.java +++ b/services/print/java/com/android/server/print/CompanionDeviceManagerService.java @@ -46,7 +46,10 @@ import android.util.ExceptionUtils; import android.util.Slog; import android.util.Xml; +import com.android.internal.content.PackageMonitor; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.CollectionUtils; +import com.android.server.FgThread; import com.android.server.SystemService; import org.xmlpull.v1.XmlPullParser; @@ -86,10 +89,35 @@ public class CompanionDeviceManagerService extends SystemService { private final CompanionDeviceManagerImpl mImpl; private final ConcurrentMap<Integer, AtomicFile> mUidToStorage = new ConcurrentHashMap<>(); + private IDeviceIdleController mIdleController; public CompanionDeviceManagerService(Context context) { super(context); mImpl = new CompanionDeviceManagerImpl(); + mIdleController = IDeviceIdleController.Stub.asInterface( + ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER)); + registerPackageMonitor(); + } + + private void registerPackageMonitor() { + new PackageMonitor() { + @Override + public void onPackageRemoved(String packageName, int uid) { + updateAssociations( + as -> CollectionUtils.filter(as, + a -> !Objects.equals(a.companionAppPackage, packageName)), + getChangingUserId()); + } + + @Override + public void onPackageModified(String packageName) { + int userId = getChangingUserId(); + if (!ArrayUtils.isEmpty(readAllAssociations(userId, packageName))) { + updateSpecialAccessPermissionForAssociatedPackage(packageName, userId); + } + } + + }.register(getContext(), FgThread.get().getLooper(), UserHandle.ALL, true); } @Override @@ -124,9 +152,9 @@ public class CompanionDeviceManagerService extends SystemService { @Override public List<String> getAssociations(String callingPackage) { - return ArrayUtils.map( + return CollectionUtils.map( readAllAssociations(getUserId(), callingPackage), - (a) -> a.deviceAddress); + a -> a.deviceAddress); } @Override @@ -178,43 +206,55 @@ public class CompanionDeviceManagerService extends SystemService { @Override public void onDeviceSelected(String packageName, int userId, String deviceAddress) { //TODO unbind - grantSpecialAccessPermissionsIfNeeded(packageName, userId); + updateSpecialAccessPermissionForAssociatedPackage(packageName, userId); recordAssociation(packageName, deviceAddress); } }; } - private void grantSpecialAccessPermissionsIfNeeded(String packageName, int userId) { - final long identity = Binder.clearCallingIdentity(); - final PackageInfo packageInfo; - try { + private void updateSpecialAccessPermissionForAssociatedPackage(String packageName, int userId) { + PackageInfo packageInfo = getPackageInfo(packageName, userId); + if (packageInfo == null) { + return; + } + + Binder.withCleanCallingIdentity(() -> { try { - packageInfo = getContext().getPackageManager().getPackageInfoAsUser( - packageName, PackageManager.GET_PERMISSIONS, userId); - } catch (PackageManager.NameNotFoundException e) { - Slog.e(LOG_TAG, "Error granting special access permissions to package:" - + packageName, e); - return; - } - if (ArrayUtils.contains(packageInfo.requestedPermissions, - Manifest.permission.RUN_IN_BACKGROUND)) { - IDeviceIdleController idleController = IDeviceIdleController.Stub.asInterface( - ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER)); - try { - idleController.addPowerSaveWhitelistApp(packageName); - } catch (RemoteException e) { - /* ignore - local call */ + if (ArrayUtils.contains(packageInfo.requestedPermissions, + Manifest.permission.RUN_IN_BACKGROUND)) { + mIdleController.addPowerSaveWhitelistApp(packageInfo.packageName); + } else { + mIdleController.removePowerSaveWhitelistApp(packageInfo.packageName); } + } catch (RemoteException e) { + /* ignore - local call */ } + + NetworkPolicyManager networkPolicyManager = NetworkPolicyManager.from(getContext()); if (ArrayUtils.contains(packageInfo.requestedPermissions, Manifest.permission.USE_DATA_IN_BACKGROUND)) { - NetworkPolicyManager.from(getContext()).addUidPolicy( + networkPolicyManager.addUidPolicy( + packageInfo.applicationInfo.uid, + NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND); + } else { + networkPolicyManager.removeUidPolicy( packageInfo.applicationInfo.uid, NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND); } - } finally { - Binder.restoreCallingIdentity(identity); - } + }); + } + + @Nullable + private PackageInfo getPackageInfo(String packageName, int userId) { + return Binder.withCleanCallingIdentity(() -> { + try { + return getContext().getPackageManager().getPackageInfoAsUser( + packageName, PackageManager.GET_PERMISSIONS, userId); + } catch (PackageManager.NameNotFoundException e) { + Slog.e(LOG_TAG, "Failed to get PackageInfo for package " + packageName, e); + return null; + } + }); } private void recordAssociation(String priviledgedPackage, String deviceAddress) { @@ -222,13 +262,16 @@ public class CompanionDeviceManagerService extends SystemService { new Association(getUserId(), deviceAddress, priviledgedPackage))); } - private void updateAssociations( - Function<ArrayList<Association>, ArrayList<Association>> update) { - final int userId = getUserId(); + private void updateAssociations(Function<ArrayList<Association>, List<Association>> update) { + updateAssociations(update, getUserId()); + } + + private void updateAssociations(Function<ArrayList<Association>, List<Association>> update, + int userId) { final AtomicFile file = getStorageFileForUser(userId); synchronized (file) { final ArrayList<Association> old = readAllAssociations(userId); - final ArrayList<Association> associations = update.apply(old); + final List<Association> associations = update.apply(old); if (Objects.equals(old, associations)) return; file.write((out) -> { @@ -239,7 +282,7 @@ public class CompanionDeviceManagerService extends SystemService { xml.startDocument(null, true); xml.startTag(null, XML_TAG_ASSOCIATIONS); - for (int i = 0; i < ArrayUtils.size(associations); i++) { + for (int i = 0; i < CollectionUtils.size(associations); i++) { Association association = associations.get(i); xml.startTag(null, XML_TAG_ASSOCIATION) .attribute(null, XML_ATTR_PACKAGE, association.companionAppPackage) diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index aa5586090808..4dda766ed8c7 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -5054,26 +5054,21 @@ public class TelephonyManager { public void onReceiveUssdResponseFailed(String request, int failureCode) {}; } - /* <p>Requires permission: - * @link android.Manifest.permission#CALL_PHONE} + /** + * Sends an Unstructured Supplementary Service Data (USSD) request to the cellular network and + * informs the caller of the response via {@code callback}. + * <p>Carriers define USSD codes which can be sent by the user to request information such as + * the user's current data balance or minutes balance. + * <p>Requires permission: + * {@link android.Manifest.permission#CALL_PHONE} * @param ussdRequest the USSD command to be executed. - * @param wrappedCallback receives a callback result. + * @param callback called by the framework to inform the caller of the result of executing the + * USSD request (see {@link OnReceiveUssdResponseCallback}). + * @param handler the {@link Handler} to run the request on. */ @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void sendUssdRequest(String ussdRequest, final OnReceiveUssdResponseCallback callback, Handler handler) { - sendUssdRequest(ussdRequest, getSubId(), callback, handler); - } - - /* <p>Requires permission: - * @link android.Manifest.permission#CALL_PHONE} - * @param subId The subscription to use. - * @param ussdRequest the USSD command to be executed. - * @param wrappedCallback receives a callback result. - */ - @RequiresPermission(android.Manifest.permission.CALL_PHONE) - public void sendUssdRequest(String ussdRequest, int subId, - final OnReceiveUssdResponseCallback callback, Handler handler) { checkNotNull(callback, "OnReceiveUssdResponseCallback cannot be null."); ResultReceiver wrappedCallback = new ResultReceiver(handler) { @@ -5095,7 +5090,7 @@ public class TelephonyManager { try { ITelephony telephony = getITelephony(); if (telephony != null) { - telephony.handleUssdRequest(subId, ussdRequest, wrappedCallback); + telephony.handleUssdRequest(mSubId, ussdRequest, wrappedCallback); } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelephony#sendUSSDCode", e); diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java index 48861bde77b3..dbc78612c399 100644 --- a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java +++ b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java @@ -26,6 +26,11 @@ import static com.android.server.connectivity.MetricsTestUtil.anIntArray; import static com.android.server.connectivity.MetricsTestUtil.b; import static com.android.server.connectivity.MetricsTestUtil.describeIpEvent; import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityLog; +import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.BLUETOOTH; +import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.CELLULAR; +import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.ETHERNET; +import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.MULTIPLE; +import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.WIFI; import android.net.ConnectivityMetricsEvent; import android.net.metrics.ApfProgramEvent; @@ -47,6 +52,135 @@ import junit.framework.TestCase; public class IpConnectivityEventBuilderTest extends TestCase { @SmallTest + public void testLinkLayerInferrence() { + ConnectivityMetricsEvent ev = describeIpEvent( + aType(IpReachabilityEvent.class), + anInt(IpReachabilityEvent.NUD_FAILED)); + + String want = joinLines( + "dropped_events: 0", + "events <", + " if_name: \"\"", + " link_layer: 0", + " network_id: 0", + " time_ms: 1", + " transports: 0", + " ip_reachability_event <", + " event_type: 512", + " if_name: \"\"", + " >", + ">", + "version: 2"); + verifySerialization(want, ev); + + ev.netId = 123; + ev.transports = 3; // transports have priority for inferrence of link layer + ev.ifname = "wlan0"; + want = joinLines( + "dropped_events: 0", + "events <", + " if_name: \"\"", + String.format(" link_layer: %d", MULTIPLE), + " network_id: 123", + " time_ms: 1", + " transports: 3", + " ip_reachability_event <", + " event_type: 512", + " if_name: \"\"", + " >", + ">", + "version: 2"); + verifySerialization(want, ev); + + ev.transports = 1; + ev.ifname = null; + want = joinLines( + "dropped_events: 0", + "events <", + " if_name: \"\"", + String.format(" link_layer: %d", CELLULAR), + " network_id: 123", + " time_ms: 1", + " transports: 1", + " ip_reachability_event <", + " event_type: 512", + " if_name: \"\"", + " >", + ">", + "version: 2"); + verifySerialization(want, ev); + + ev.transports = 0; + ev.ifname = "not_inferred"; + want = joinLines( + "dropped_events: 0", + "events <", + " if_name: \"not_inferred\"", + " link_layer: 0", + " network_id: 123", + " time_ms: 1", + " transports: 0", + " ip_reachability_event <", + " event_type: 512", + " if_name: \"\"", + " >", + ">", + "version: 2"); + verifySerialization(want, ev); + + ev.ifname = "bt-pan"; + want = joinLines( + "dropped_events: 0", + "events <", + " if_name: \"\"", + String.format(" link_layer: %d", BLUETOOTH), + " network_id: 123", + " time_ms: 1", + " transports: 0", + " ip_reachability_event <", + " event_type: 512", + " if_name: \"\"", + " >", + ">", + "version: 2"); + verifySerialization(want, ev); + + ev.ifname = "rmnet_ipa0"; + want = joinLines( + "dropped_events: 0", + "events <", + " if_name: \"\"", + String.format(" link_layer: %d", CELLULAR), + " network_id: 123", + " time_ms: 1", + " transports: 0", + " ip_reachability_event <", + " event_type: 512", + " if_name: \"\"", + " >", + ">", + "version: 2"); + verifySerialization(want, ev); + + ev.ifname = "wlan0"; + want = joinLines( + "dropped_events: 0", + "events <", + " if_name: \"\"", + String.format(" link_layer: %d", WIFI), + " network_id: 123", + " time_ms: 1", + " transports: 0", + " ip_reachability_event <", + " event_type: 512", + " if_name: \"\"", + " >", + ">", + "version: 2"); + verifySerialization(want, ev); + } + + @SmallTest public void testDefaultNetworkEventSerialization() { ConnectivityMetricsEvent ev = describeIpEvent( aType(DefaultNetworkEvent.class), @@ -86,7 +220,6 @@ public class IpConnectivityEventBuilderTest extends TestCase { public void testDhcpClientEventSerialization() { ConnectivityMetricsEvent ev = describeIpEvent( aType(DhcpClientEvent.class), - aString("wlan0"), aString("SomeState"), anInt(192)); @@ -100,7 +233,7 @@ public class IpConnectivityEventBuilderTest extends TestCase { " transports: 0", " dhcp_event <", " duration_ms: 192", - " if_name: \"wlan0\"", + " if_name: \"\"", " state_transition: \"SomeState\"", " >", ">", @@ -113,7 +246,6 @@ public class IpConnectivityEventBuilderTest extends TestCase { public void testDhcpErrorEventSerialization() { ConnectivityMetricsEvent ev = describeIpEvent( aType(DhcpErrorEvent.class), - aString("wlan0"), anInt(DhcpErrorEvent.L4_NOT_UDP)); String want = joinLines( @@ -126,7 +258,7 @@ public class IpConnectivityEventBuilderTest extends TestCase { " transports: 0", " dhcp_event <", " duration_ms: 0", - " if_name: \"wlan0\"", + " if_name: \"\"", " error_code: 50397184", " >", ">", @@ -191,7 +323,6 @@ public class IpConnectivityEventBuilderTest extends TestCase { public void testIpManagerEventSerialization() { ConnectivityMetricsEvent ev = describeIpEvent( aType(IpManagerEvent.class), - aString("wlan0"), anInt(IpManagerEvent.PROVISIONING_OK), aLong(5678)); @@ -205,7 +336,7 @@ public class IpConnectivityEventBuilderTest extends TestCase { " transports: 0", " ip_provisioning_event <", " event_type: 1", - " if_name: \"wlan0\"", + " if_name: \"\"", " latency_ms: 5678", " >", ">", @@ -218,7 +349,6 @@ public class IpConnectivityEventBuilderTest extends TestCase { public void testIpReachabilityEventSerialization() { ConnectivityMetricsEvent ev = describeIpEvent( aType(IpReachabilityEvent.class), - aString("wlan0"), anInt(IpReachabilityEvent.NUD_FAILED)); String want = joinLines( @@ -231,7 +361,7 @@ public class IpConnectivityEventBuilderTest extends TestCase { " transports: 0", " ip_reachability_event <", " event_type: 512", - " if_name: \"wlan0\"", + " if_name: \"\"", " >", ">", "version: 2"); diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java index 785e1ce20ab5..50c92b3804c1 100644 --- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java +++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java @@ -48,7 +48,7 @@ import org.mockito.MockitoAnnotations; public class IpConnectivityMetricsTest extends TestCase { static final IpReachabilityEvent FAKE_EV = - new IpReachabilityEvent("wlan0", IpReachabilityEvent.NUD_FAILED); + new IpReachabilityEvent(IpReachabilityEvent.NUD_FAILED); @Mock Context mCtx; @Mock IIpConnectivityMetrics mMockService; @@ -154,47 +154,51 @@ public class IpConnectivityMetricsTest extends TestCase { apfStats.programUpdatesAllowingMulticast = 3; apfStats.maxProgramSize = 2048; Parcelable[] events = { - new IpReachabilityEvent("wlan0", IpReachabilityEvent.NUD_FAILED), - new DhcpClientEvent("wlan0", "SomeState", 192), + new IpReachabilityEvent(IpReachabilityEvent.NUD_FAILED), + new DhcpClientEvent("SomeState", 192), new DefaultNetworkEvent(102, new int[]{1,2,3}, 101, true, false), - new IpManagerEvent("wlan0", IpManagerEvent.PROVISIONING_OK, 5678), + new IpManagerEvent(IpManagerEvent.PROVISIONING_OK, 5678), new ValidationProbeEvent(120, 40730, ValidationProbeEvent.PROBE_HTTP, 204), apfStats, new RaEvent(2000, 400, 300, -1, 1000, -1) }; for (int i = 0; i < events.length; i++) { - logger.log(100 * (i + 1), events[i]); + ConnectivityMetricsEvent ev = new ConnectivityMetricsEvent(); + ev.timestamp = 100 * (i + 1); + ev.ifname = "wlan0"; + ev.data = events[i]; + logger.log(ev); } String want = joinLines( "dropped_events: 0", "events <", " if_name: \"\"", - " link_layer: 0", + " link_layer: 4", " network_id: 0", " time_ms: 100", " transports: 0", " ip_reachability_event <", " event_type: 512", - " if_name: \"wlan0\"", + " if_name: \"\"", " >", ">", "events <", " if_name: \"\"", - " link_layer: 0", + " link_layer: 4", " network_id: 0", " time_ms: 200", " transports: 0", " dhcp_event <", " duration_ms: 192", - " if_name: \"wlan0\"", + " if_name: \"\"", " state_transition: \"SomeState\"", " >", ">", "events <", " if_name: \"\"", - " link_layer: 0", + " link_layer: 4", " network_id: 0", " time_ms: 300", " transports: 0", @@ -213,19 +217,19 @@ public class IpConnectivityMetricsTest extends TestCase { ">", "events <", " if_name: \"\"", - " link_layer: 0", + " link_layer: 4", " network_id: 0", " time_ms: 400", " transports: 0", " ip_provisioning_event <", " event_type: 1", - " if_name: \"wlan0\"", + " if_name: \"\"", " latency_ms: 5678", " >", ">", "events <", " if_name: \"\"", - " link_layer: 0", + " link_layer: 4", " network_id: 0", " time_ms: 500", " transports: 0", @@ -240,7 +244,7 @@ public class IpConnectivityMetricsTest extends TestCase { ">", "events <", " if_name: \"\"", - " link_layer: 0", + " link_layer: 4", " network_id: 0", " time_ms: 600", " transports: 0", @@ -259,7 +263,7 @@ public class IpConnectivityMetricsTest extends TestCase { ">", "events <", " if_name: \"\"", - " link_layer: 0", + " link_layer: 4", " network_id: 0", " time_ms: 700", " transports: 0", diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp index 3c3edda85e4f..13ae24b7d29e 100644 --- a/tools/aapt/AaptAssets.cpp +++ b/tools/aapt/AaptAssets.cpp @@ -964,7 +964,6 @@ void AaptAssets::addResource(const String8& leafName, const String8& path, subdir->addFile(leafName, grr); } - ssize_t AaptAssets::slurpFromArgs(Bundle* bundle) { int count; diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index 15648bdd1b3b..fdcc7aa03775 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -2501,14 +2501,21 @@ static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilde const size_t numConfigs = gp->getFiles().size(); for (size_t j = 0; j < numConfigs; j++) { status_t err = NO_ERROR; + const sp<AaptFile>& file = gp->getFiles().valueAt(j); + if (!file->hasData()) { + // Empty files do not get written. + continue; + } + if (ignoreConfig) { - err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j)); + err = builder->getBaseSplit()->addEntry(gp->getPath(), file); } else { - err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j)); + err = builder->addEntry(gp->getPath(), file); } + if (err != NO_ERROR) { fprintf(stderr, "Failed to add %s (%s) to builder.\n", - gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string()); + gp->getPath().string(), file->getPrintableSource().string()); return err; } } diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index 3330b1a78c1e..2bf52066b618 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -1403,7 +1403,8 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuil String8 src = it.getFile()->getPrintableSource(); err = compileXmlFile(bundle, assets, String16(it.getBaseName()), it.getFile(), &table, xmlFlags); - if (err == NO_ERROR) { + // Only verify IDs if there was no error and the file is non-empty. + if (err == NO_ERROR && it.getFile()->hasData()) { ResXMLTree block; block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true); checkForIds(src, block); @@ -1550,7 +1551,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuil String8 src = it.getFile()->getPrintableSource(); err = compileXmlFile(bundle, assets, String16(it.getBaseName()), it.getFile(), &table, xmlFlags); - if (err == NO_ERROR) { + if (err == NO_ERROR && it.getFile()->hasData()) { ResXMLTree block; block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true); checkForIds(src, block); @@ -1598,7 +1599,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuil err = compileXmlFile(bundle, assets, workItem.resourceName, workItem.xmlRoot, workItem.file, &table, xmlCompilationFlags); - if (err == NO_ERROR) { + if (err == NO_ERROR && workItem.file->hasData()) { assets->addResource(workItem.resPath.getPathLeaf(), workItem.resPath, workItem.file, diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index 60f0d56e09bf..619ae6228318 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -78,6 +78,13 @@ status_t compileXmlFile(const Bundle* bundle, ResourceTable* table, int options) { + if (table->versionForCompat(bundle, resourceName, target, root)) { + // The file was versioned, so stop processing here. + // The resource entry has already been removed and the new one added. + // The `target` file will be empty, but empty files do not get written to the APK. + return NO_ERROR; + } + if ((options&XML_COMPILE_STRIP_WHITESPACE) != 0) { root->removeWhitespace(true, NULL); } else if ((options&XML_COMPILE_COMPACT_WHITESPACE) != 0) { @@ -4758,6 +4765,77 @@ static bool IsTransitionElement(const String16& name) { return false; } +bool ResourceTable::versionForCompat(const Bundle* bundle, const String16& resourceName, + const sp<AaptFile>& target, const sp<XMLNode>& root) { + XMLNode* node = root.get(); + while (node->getType() != XMLNode::TYPE_ELEMENT) { + // We're assuming the root element is what we're looking for, which can only be under a + // bunch of namespace declarations. + if (node->getChildren().size() != 1) { + // Not sure what to do, bail. + return false; + } + node = node->getChildren().itemAt(0).get(); + } + + if (node->getElementNamespace().size() != 0) { + // Not something we care about. + return false; + } + + int versionedSdk = 0; + if (node->getElementName() == String16("adaptive-icon")) { + versionedSdk = SDK_O; + } + + const int minSdkVersion = getMinSdkVersion(bundle); + const ConfigDescription config(target->getGroupEntry().toParams()); + if (versionedSdk <= minSdkVersion || versionedSdk <= config.sdkVersion) { + return false; + } + + sp<ConfigList> cl = getConfigList(String16(mAssets->getPackage()), + String16(target->getResourceType()), resourceName); + if (!shouldGenerateVersionedResource(cl, config, versionedSdk)) { + return false; + } + + // Remove the original entry. + cl->removeEntry(config); + + // We need to wholesale version this file. + ConfigDescription newConfig(config); + newConfig.sdkVersion = versionedSdk; + sp<AaptFile> newFile = new AaptFile(target->getSourceFile(), + AaptGroupEntry(newConfig), target->getResourceType()); + String8 resPath = String8::format("res/%s/%s.xml", + newFile->getGroupEntry().toDirName(target->getResourceType()).string(), + String8(resourceName).string()); + resPath.convertToResPath(); + + // Add a resource table entry. + addEntry(SourcePos(), + String16(mAssets->getPackage()), + String16(target->getResourceType()), + resourceName, + String16(resPath), + NULL, + &newConfig); + + // Schedule this to be compiled. + CompileResourceWorkItem item; + item.resourceName = resourceName; + item.resPath = resPath; + item.file = newFile; + item.xmlRoot = root->clone(); + item.needsCompiling = false; // This step occurs after we parse/assign, so we don't need + // to do it again. + mWorkQueue.push(item); + + // Now mark the old entry as deleted. + return true; +} + status_t ResourceTable::modifyForCompat(const Bundle* bundle, const String16& resourceName, const sp<AaptFile>& target, diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h index cf1e992ec330..aff22d4d1364 100644 --- a/tools/aapt/ResourceTable.h +++ b/tools/aapt/ResourceTable.h @@ -203,6 +203,9 @@ public: size_t numLocalResources() const; bool hasResources() const; + bool versionForCompat(const Bundle* bundle, const String16& resourceName, + const sp<AaptFile>& file, const sp<XMLNode>& root); + status_t modifyForCompat(const Bundle* bundle); status_t modifyForCompat(const Bundle* bundle, const String16& resourceName, @@ -431,6 +434,10 @@ public: mEntries.add(config, entry); } + void removeEntry(const ResTable_config& config) { + mEntries.removeItem(config); + } + const DefaultKeyedVector<ConfigDescription, sp<Entry> >& getEntries() const { return mEntries; } private: const String16 mName; |