summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt12
-rw-r--r--api/removed.txt4
-rw-r--r--api/system-current.txt18
-rw-r--r--api/system-removed.txt4
-rw-r--r--api/test-current.txt12
-rw-r--r--api/test-removed.txt4
-rw-r--r--core/java/android/app/Activity.java31
-rw-r--r--core/java/android/app/ActivityManager.java29
-rw-r--r--core/java/android/app/AppOpsManager.java17
-rw-r--r--core/java/android/app/EphemeralResolverService.java21
-rw-r--r--core/java/android/app/IActivityManager.aidl1
-rw-r--r--core/java/android/app/IInstantAppResolver.aidl4
-rw-r--r--core/java/android/app/ITaskStackListener.aidl5
-rw-r--r--core/java/android/app/InstantAppResolverService.java74
-rw-r--r--core/java/android/app/Notification.java2
-rw-r--r--core/java/android/app/RecoverableSecurityException.java57
-rw-r--r--core/java/android/app/TaskStackListener.java6
-rw-r--r--core/java/android/bluetooth/IBluetoothGatt.aidl4
-rw-r--r--core/java/android/bluetooth/le/AdvertisingSet.java6
-rw-r--r--core/java/android/bluetooth/le/AdvertisingSetCallback.java8
-rw-r--r--core/java/android/bluetooth/le/BluetoothLeAdvertiser.java10
-rw-r--r--core/java/android/bluetooth/le/IAdvertisingSetCallback.aidl4
-rw-r--r--core/java/android/companion/AssociationRequest.java3
-rw-r--r--core/java/android/companion/BluetoothDeviceFilter.java5
-rw-r--r--core/java/android/content/pm/ShortcutManager.java207
-rw-r--r--core/java/android/content/res/AssetManager.java5
-rw-r--r--core/java/android/net/ConnectivityMetricsEvent.java10
-rw-r--r--core/java/android/net/NetworkCapabilities.java6
-rw-r--r--core/java/android/net/metrics/DhcpClientEvent.java8
-rw-r--r--core/java/android/net/metrics/DhcpErrorEvent.java8
-rw-r--r--core/java/android/net/metrics/IpConnectivityLog.java12
-rw-r--r--core/java/android/net/metrics/IpManagerEvent.java10
-rw-r--r--core/java/android/net/metrics/IpReachabilityEvent.java8
-rw-r--r--core/java/android/os/Binder.java31
-rw-r--r--core/java/android/os/HandlerThread.java16
-rwxr-xr-xcore/java/android/provider/Settings.java3
-rw-r--r--core/java/android/view/View.java15
-rw-r--r--core/java/android/view/ViewStructure.java4
-rw-r--r--core/java/com/android/internal/util/ArrayUtils.java53
-rw-r--r--core/java/com/android/internal/util/CollectionUtils.java130
-rw-r--r--core/java/com/android/server/BootReceiver.java52
-rw-r--r--core/res/res/values/config.xml2
-rw-r--r--core/tests/coretests/AndroidManifest.xml3
-rw-r--r--core/tests/coretests/src/android/os/SetPersistentVrThreadTest.java162
-rw-r--r--libs/hwui/SkiaCanvas.cpp39
-rw-r--r--libs/hwui/SkiaCanvasProxy.cpp17
-rw-r--r--libs/hwui/SkiaCanvasProxy.h5
-rw-r--r--libs/hwui/tests/unit/FatalTestCanvas.h4
-rwxr-xr-xmedia/java/android/mtp/MtpDatabase.java9
-rw-r--r--media/jni/android_mtp_MtpDatabase.cpp11
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java14
-rw-r--r--packages/ExternalStorageProvider/AndroidManifest.xml1
-rw-r--r--packages/ExternalStorageProvider/res/values/strings.xml3
-rw-r--r--packages/SettingsLib/res/values/strings.xml6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java12
-rw-r--r--packages/SystemUI/res/drawable/pip_notification_icon.xml25
-rw-r--r--packages/SystemUI/res/values/strings.xml15
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java142
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java92
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java126
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationIconDozeHelper.java101
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java18
-rw-r--r--proto/src/metrics_constants.proto8
-rw-r--r--services/core/java/com/android/server/AlarmManagerService.java12
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java12
-rw-r--r--services/core/java/com/android/server/DeviceIdleController.java6
-rw-r--r--services/core/java/com/android/server/LockSettingsService.java9
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java204
-rw-r--r--services/core/java/com/android/server/am/ActivityRecord.java19
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java5
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java37
-rw-r--r--services/core/java/com/android/server/am/ActivityStarter.java71
-rw-r--r--services/core/java/com/android/server/am/TaskChangeNotificationController.java23
-rw-r--r--services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java91
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java116
-rw-r--r--services/core/java/com/android/server/pm/EphemeralResolverConnection.java19
-rw-r--r--services/core/java/com/android/server/pm/InstantAppResolver.java29
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java15
-rw-r--r--services/core/java/com/android/server/power/Notifier.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java54
-rw-r--r--services/java/com/android/server/SystemServer.java18
-rw-r--r--services/net/java/android/net/dhcp/DhcpClient.java4
-rw-r--r--services/net/java/android/net/ip/IpManager.java2
-rw-r--r--services/net/java/android/net/ip/IpReachabilityMonitor.java4
-rw-r--r--services/print/java/com/android/server/print/CompanionDeviceManagerService.java105
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java27
-rw-r--r--tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java146
-rw-r--r--tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java34
-rw-r--r--tools/aapt/AaptAssets.cpp1
-rw-r--r--tools/aapt/Command.cpp13
-rw-r--r--tools/aapt/Resource.cpp7
-rw-r--r--tools/aapt/ResourceTable.cpp78
-rw-r--r--tools/aapt/ResourceTable.h7
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&mdash;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 &ndash; 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 &ndash; 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>&lt;intent-filter&gt;</code>
+ * element, as shown in the following snippet:
+ *
+ * <pre>
+ *&lt;manifest&gt;
+ * ...
+ * &lt;application&gt;
+ * &lt;activity android:name="com.example.MyCustomPromptToPinShortcut" ... &gt;
+ * &lt;intent-filter
+ * action android:name="android.intent.action.ACTION_CREATE_SHORTCUT"&gt;
+ * ...
+ * &lt;/intent-filter&gt;
+ * &lt;/activity&gt;
+ * ...
+ * &lt;/application&gt;
+ *&lt;/manifest&gt;
+ * </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;