Merge "Fix the huge value of the ACTION_DASHBOARD_VISIBLE_TIME"
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
index 41347cc..adc16d2 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -844,13 +844,6 @@
pulledData.add(e);
}
- private void pullSystemUpTime(int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeLong(SystemClock.uptimeMillis());
- pulledData.add(e);
- }
-
private void pullProcessMemoryState(
int tagId, long elapsedNanos, long wallClockNanos,
List<StatsLogEventWrapper> pulledData) {
@@ -2049,11 +2042,6 @@
break;
}
- case StatsLog.SYSTEM_UPTIME: {
- pullSystemUpTime(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
-
case StatsLog.SYSTEM_ELAPSED_REALTIME: {
pullSystemElapsedRealtime(tagId, elapsedNanos, wallClockNanos, ret);
break;
diff --git a/api/current.txt b/api/current.txt
index e166c14..16786ff 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -386,6 +386,7 @@
field public static final int canRequestFingerprintGestures = 16844109; // 0x101054d
field public static final int canRequestTouchExplorationMode = 16843735; // 0x10103d7
field public static final int canRetrieveWindowContent = 16843653; // 0x1010385
+ field public static final int canTakeScreenshot = 16844304; // 0x1010610
field public static final int candidatesTextStyleSpans = 16843312; // 0x1010230
field public static final int cantSaveState = 16844142; // 0x101056e
field @Deprecated public static final int capitalize = 16843113; // 0x1010169
@@ -2864,6 +2865,7 @@
method protected void onServiceConnected();
method public final boolean performGlobalAction(int);
method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo);
+ method public boolean takeScreenshot(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.graphics.Bitmap>);
field public static final int GESTURE_SWIPE_DOWN = 2; // 0x2
field public static final int GESTURE_SWIPE_DOWN_AND_LEFT = 15; // 0xf
field public static final int GESTURE_SWIPE_DOWN_AND_RIGHT = 16; // 0x10
@@ -2960,6 +2962,7 @@
field public static final int CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES = 64; // 0x40
field public static final int CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 2; // 0x2
field public static final int CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT = 1; // 0x1
+ field public static final int CAPABILITY_CAN_TAKE_SCREENSHOT = 128; // 0x80
field @NonNull public static final android.os.Parcelable.Creator<android.accessibilityservice.AccessibilityServiceInfo> CREATOR;
field public static final int DEFAULT = 1; // 0x1
field public static final int FEEDBACK_ALL_MASK = -1; // 0xffffffff
@@ -11418,6 +11421,7 @@
public final class InstallSourceInfo implements android.os.Parcelable {
method public int describeContents();
method @Nullable public String getInitiatingPackageName();
+ method @Nullable public android.content.pm.SigningInfo getInitiatingPackageSigningInfo();
method @Nullable public String getInstallingPackageName();
method @Nullable public String getOriginatingPackageName();
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -58904,7 +58908,7 @@
method @android.view.ViewDebug.ExportedProperty public CharSequence getFormat24Hour();
method public String getTimeZone();
method public boolean is24HourModeEnabled();
- method public void refresh();
+ method public void refreshTime();
method public void setFormat12Hour(CharSequence);
method public void setFormat24Hour(CharSequence);
method public void setTimeZone(String);
@@ -59219,6 +59223,7 @@
public class Toast {
ctor public Toast(android.content.Context);
+ method public void addCallback(@NonNull android.widget.Toast.Callback);
method public void cancel();
method public int getDuration();
method public int getGravity();
@@ -59229,6 +59234,7 @@
method public int getYOffset();
method public static android.widget.Toast makeText(android.content.Context, CharSequence, int);
method public static android.widget.Toast makeText(android.content.Context, @StringRes int, int) throws android.content.res.Resources.NotFoundException;
+ method public void removeCallback(@NonNull android.widget.Toast.Callback);
method public void setDuration(int);
method public void setGravity(int, int, int);
method public void setMargin(float, float);
@@ -59240,6 +59246,12 @@
field public static final int LENGTH_SHORT = 0; // 0x0
}
+ public abstract static class Toast.Callback {
+ ctor public Toast.Callback();
+ method public void onToastHidden();
+ method public void onToastShown();
+ }
+
public class ToggleButton extends android.widget.CompoundButton {
ctor public ToggleButton(android.content.Context, android.util.AttributeSet, int, int);
ctor public ToggleButton(android.content.Context, android.util.AttributeSet, int);
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 0a67065..c8253a0 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -79,3 +79,48 @@
}
+package android.timezone {
+
+ public final class CountryTimeZones {
+ method @Nullable public android.icu.util.TimeZone getDefaultTimeZone();
+ method @Nullable public String getDefaultTimeZoneId();
+ method @NonNull public java.util.List<android.timezone.CountryTimeZones.TimeZoneMapping> getEffectiveTimeZoneMappingsAt(long);
+ method public boolean hasUtcZone(long);
+ method public boolean isDefaultTimeZoneBoosted();
+ method public boolean isForCountryCode(@NonNull String);
+ method @Nullable public android.timezone.CountryTimeZones.OffsetResult lookupByOffsetWithBias(int, @Nullable Boolean, @Nullable Integer, long, @Nullable android.icu.util.TimeZone);
+ }
+
+ public static final class CountryTimeZones.OffsetResult {
+ ctor public CountryTimeZones.OffsetResult(@NonNull android.icu.util.TimeZone, boolean);
+ method @NonNull public android.icu.util.TimeZone getTimeZone();
+ method public boolean isOnlyMatch();
+ }
+
+ public static final class CountryTimeZones.TimeZoneMapping {
+ method @Nullable public android.icu.util.TimeZone getTimeZone();
+ method @NonNull public String getTimeZoneId();
+ }
+
+ public class TelephonyLookup {
+ method @NonNull public static android.timezone.TelephonyLookup getInstance();
+ method @Nullable public android.timezone.TelephonyNetworkFinder getTelephonyNetworkFinder();
+ }
+
+ public class TelephonyNetwork {
+ method @NonNull public String getCountryIsoCode();
+ method @NonNull public String getMcc();
+ method @NonNull public String getMnc();
+ }
+
+ public class TelephonyNetworkFinder {
+ method @Nullable public android.timezone.TelephonyNetwork findNetworkByMccMnc(@NonNull String, @NonNull String);
+ }
+
+ public final class TimeZoneFinder {
+ method @NonNull public static android.timezone.TimeZoneFinder getInstance();
+ method @Nullable public android.timezone.CountryTimeZones lookupCountryTimeZones(@NonNull String);
+ }
+
+}
+
diff --git a/api/system-current.txt b/api/system-current.txt
index 3f34096..056fb08 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -791,6 +791,7 @@
package android.app.admin {
public class DevicePolicyManager {
+ method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean getBluetoothContactSharingDisabled(@NonNull android.os.UserHandle);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwner();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.content.ComponentName getDeviceOwnerComponentOnAnyUser();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwnerNameOnAnyUser();
@@ -1037,6 +1038,7 @@
field public static final int AGENT_ERROR = -1003; // 0xfffffc15
field public static final int AGENT_UNKNOWN = -1004; // 0xfffffc14
field public static final String EXTRA_TRANSPORT_REGISTRATION = "android.app.backup.extra.TRANSPORT_REGISTRATION";
+ field public static final int FLAG_DATA_NOT_CHANGED = 8; // 0x8
field public static final int FLAG_INCREMENTAL = 2; // 0x2
field public static final int FLAG_NON_INCREMENTAL = 4; // 0x4
field public static final int FLAG_USER_INITIATED = 1; // 0x1
@@ -1703,7 +1705,7 @@
method public abstract void sendBroadcast(android.content.Intent, @Nullable String, @Nullable android.os.Bundle);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public abstract void sendBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle, @Nullable String, @Nullable android.os.Bundle);
method public abstract void sendOrderedBroadcast(@NonNull android.content.Intent, @Nullable String, @Nullable android.os.Bundle, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
- method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.os.UserHandle);
+ method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.os.UserHandle);
field public static final String APP_INTEGRITY_SERVICE = "app_integrity";
field public static final String APP_PREDICTION_SERVICE = "app_prediction";
field public static final String BACKUP_SERVICE = "backup";
@@ -4820,6 +4822,7 @@
field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
field public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb
field public static final int TYPE_NONE = -1; // 0xffffffff
+ field @Deprecated public static final int TYPE_WIFI_P2P = 13; // 0xd
}
public abstract static class ConnectivityManager.OnStartTetheringCallback {
@@ -4968,6 +4971,27 @@
field public final int netId;
}
+ public abstract class NetworkAgent {
+ method public void onAddKeepalivePacketFilter(int, @NonNull android.net.KeepalivePacketData);
+ method public void onAutomaticReconnectDisabled();
+ method public void onBandwidthUpdateRequested();
+ method public void onNetworkUnwanted();
+ method public void onRemoveKeepalivePacketFilter(int);
+ method public void onSaveAcceptUnvalidated(boolean);
+ method public void onSignalStrengthThresholdsUpdated(@NonNull int[]);
+ method public void onStartSocketKeepalive(int, int, @NonNull android.net.KeepalivePacketData);
+ method public void onStopSocketKeepalive(int);
+ method public void onValidationStatus(int, @Nullable String);
+ method public void sendLinkProperties(@NonNull android.net.LinkProperties);
+ method public void sendNetworkCapabilities(@NonNull android.net.NetworkCapabilities);
+ method public void sendNetworkScore(int);
+ method public void sendSocketKeepaliveEvent(int, int);
+ field public static final int VALIDATION_STATUS_NOT_VALID = 2; // 0x2
+ field public static final int VALIDATION_STATUS_VALID = 1; // 0x1
+ field @NonNull public final android.net.Network network;
+ field public final int providerId;
+ }
+
public final class NetworkAgentConfig implements android.os.Parcelable {
method public int describeContents();
method @Nullable public String getSubscriberId();
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index e37a549..37fbf39 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -87,10 +87,6 @@
.pullTimeoutNs = NS_PER_SEC / 2,
}},
- // system_uptime
- {{.atomTag = android::util::SYSTEM_UPTIME},
- {.puller = new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
-
// remaining_battery_capacity
{{.atomTag = android::util::REMAINING_BATTERY_CAPACITY},
{.puller = new ResourceHealthManagerPuller(android::util::REMAINING_BATTERY_CAPACITY)}},
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index e46840c..0bd8ce6 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -17,6 +17,7 @@
package android.accessibilityservice;
import android.accessibilityservice.GestureDescription.MotionEventGenerator;
+import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -26,12 +27,15 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
+import android.graphics.Bitmap;
import android.graphics.Region;
+import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.RemoteCallback;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Log;
@@ -48,10 +52,13 @@
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
+import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
* Accessibility services should only be used to assist users with disabilities in using
@@ -483,6 +490,9 @@
private FingerprintGestureController mFingerprintGestureController;
+ /** @hide */
+ public static final String KEY_ACCESSIBILITY_SCREENSHOT = "screenshot";
+
/**
* Callback for {@link android.view.accessibility.AccessibilityEvent}s.
*
@@ -1761,6 +1771,51 @@
}
/**
+ * Takes a screenshot of the specified display and returns it by {@link Bitmap.Config#HARDWARE}
+ * format.
+ * <p>
+ * <strong>Note:</strong> In order to take screenshot your service has
+ * to declare the capability to take screenshot by setting the
+ * {@link android.R.styleable#AccessibilityService_canTakeScreenshot}
+ * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
+ * Besides, This API is only supported for default display now
+ * {@link Display#DEFAULT_DISPLAY}.
+ * </p>
+ *
+ * @param displayId The logic display id, must be {@link Display#DEFAULT_DISPLAY} for
+ * default display.
+ * @param executor Executor on which to run the callback.
+ * @param callback The callback invoked when the taking screenshot is done.
+ *
+ * @return {@code true} if the taking screenshot accepted, {@code false} if not.
+ */
+ public boolean takeScreenshot(int displayId, @NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<Bitmap> callback) {
+ Preconditions.checkNotNull(executor, "executor cannot be null");
+ Preconditions.checkNotNull(callback, "callback cannot be null");
+ final IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getInstance().getConnection(
+ mConnectionId);
+ if (connection == null) {
+ return false;
+ }
+ try {
+ connection.takeScreenshotWithCallback(displayId, new RemoteCallback((result) -> {
+ final Bitmap screenshot = result.getParcelable(KEY_ACCESSIBILITY_SCREENSHOT);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> callback.accept(screenshot));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }));
+ } catch (RemoteException re) {
+ throw new RuntimeException(re);
+ }
+ return true;
+ }
+
+ /**
* Implement to return the implementation of the internal accessibility
* service interface.
*/
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 5e2c1fa..12f2c3b 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -86,6 +86,7 @@
* @attr ref android.R.styleable#AccessibilityService_settingsActivity
* @attr ref android.R.styleable#AccessibilityService_nonInteractiveUiTimeout
* @attr ref android.R.styleable#AccessibilityService_interactiveUiTimeout
+ * @attr ref android.R.styleable#AccessibilityService_canTakeScreenshot
* @see AccessibilityService
* @see android.view.accessibility.AccessibilityEvent
* @see android.view.accessibility.AccessibilityManager
@@ -136,6 +137,12 @@
*/
public static final int CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES = 0x00000040;
+ /**
+ * Capability: This accessibility service can take screenshot.
+ * @see android.R.styleable#AccessibilityService_canTakeScreenshot
+ */
+ public static final int CAPABILITY_CAN_TAKE_SCREENSHOT = 0x00000080;
+
private static SparseArray<CapabilityInfo> sAvailableCapabilityInfos;
/**
@@ -625,6 +632,10 @@
.AccessibilityService_canRequestFingerprintGestures, false)) {
mCapabilities |= CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES;
}
+ if (asAttributes.getBoolean(com.android.internal.R.styleable
+ .AccessibilityService_canTakeScreenshot, false)) {
+ mCapabilities |= CAPABILITY_CAN_TAKE_SCREENSHOT;
+ }
TypedValue peekedValue = asAttributes.peekValue(
com.android.internal.R.styleable.AccessibilityService_description);
if (peekedValue != null) {
@@ -794,6 +805,7 @@
* @see #CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS
* @see #CAPABILITY_CAN_CONTROL_MAGNIFICATION
* @see #CAPABILITY_CAN_PERFORM_GESTURES
+ * @see #CAPABILITY_CAN_TAKE_SCREENSHOT
*/
public int getCapabilities() {
return mCapabilities;
@@ -810,6 +822,7 @@
* @see #CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS
* @see #CAPABILITY_CAN_CONTROL_MAGNIFICATION
* @see #CAPABILITY_CAN_PERFORM_GESTURES
+ * @see #CAPABILITY_CAN_TAKE_SCREENSHOT
*
* @hide
*/
@@ -1253,6 +1266,8 @@
return "CAPABILITY_CAN_PERFORM_GESTURES";
case CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES:
return "CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES";
+ case CAPABILITY_CAN_TAKE_SCREENSHOT:
+ return "CAPABILITY_CAN_TAKE_SCREENSHOT";
default:
return "UNKNOWN";
}
@@ -1314,6 +1329,10 @@
new CapabilityInfo(CAPABILITY_CAN_PERFORM_GESTURES,
R.string.capability_title_canPerformGestures,
R.string.capability_desc_canPerformGestures));
+ sAvailableCapabilityInfos.put(CAPABILITY_CAN_TAKE_SCREENSHOT,
+ new CapabilityInfo(CAPABILITY_CAN_TAKE_SCREENSHOT,
+ R.string.capability_title_canTakeScreenshot,
+ R.string.capability_desc_canTakeScreenshot));
if ((context == null) || fingerprintAvailable(context)) {
sAvailableCapabilityInfos.put(CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES,
new CapabilityInfo(CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES,
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 932d0a4..4ea5c62 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -18,8 +18,10 @@
import android.accessibilityservice.AccessibilityServiceInfo;
import android.content.pm.ParceledListSlice;
+import android.graphics.Bitmap;
import android.graphics.Region;
import android.os.Bundle;
+import android.os.RemoteCallback;
import android.view.MagnificationSpec;
import android.view.MotionEvent;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -104,4 +106,8 @@
IBinder getOverlayWindowToken(int displayid);
int getWindowIdForLeashToken(IBinder token);
+
+ Bitmap takeScreenshot(int displayId);
+
+ void takeScreenshotWithCallback(int displayId, in RemoteCallback callback);
}
diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl
index 8c3180b..80ba464 100644
--- a/core/java/android/app/IUiAutomationConnection.aidl
+++ b/core/java/android/app/IUiAutomationConnection.aidl
@@ -39,7 +39,6 @@
boolean injectInputEvent(in InputEvent event, boolean sync);
void syncInputTransactions();
boolean setRotation(int rotation);
- Bitmap takeScreenshot(in Rect crop, int rotation);
boolean clearWindowContentFrameStats(int windowId);
WindowContentFrameStats getWindowContentFrameStats(int windowId);
void clearWindowAnimationFrameStats();
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 18a3e6e..2579bd1 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -27,10 +27,7 @@
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Bitmap;
-import android.graphics.Point;
-import android.graphics.Rect;
import android.graphics.Region;
-import android.hardware.display.DisplayManagerGlobal;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
@@ -831,39 +828,20 @@
}
/**
- * Takes a screenshot.
+ * Takes a screenshot of the default display and returns it by {@link Bitmap.Config#HARDWARE}
+ * format.
*
* @return The screenshot bitmap on success, null otherwise.
*/
public Bitmap takeScreenshot() {
+ final int connectionId;
synchronized (mLock) {
throwIfNotConnectedLocked();
+ connectionId = mConnectionId;
}
- Display display = DisplayManagerGlobal.getInstance()
- .getRealDisplay(Display.DEFAULT_DISPLAY);
- Point displaySize = new Point();
- display.getRealSize(displaySize);
-
- int rotation = display.getRotation();
-
- // Take the screenshot
- Bitmap screenShot = null;
- try {
- // Calling out without a lock held.
- screenShot = mUiAutomationConnection.takeScreenshot(
- new Rect(0, 0, displaySize.x, displaySize.y), rotation);
- if (screenShot == null) {
- return null;
- }
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error while taking screnshot!", re);
- return null;
- }
-
- // Optimization
- screenShot.setHasAlpha(false);
-
- return screenShot;
+ // Calling out without a lock held.
+ return AccessibilityInteractionClient.getInstance()
+ .takeScreenshot(connectionId, Display.DEFAULT_DISPLAY);
}
/**
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 82e9881..4fb9743 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -21,8 +21,6 @@
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Rect;
import android.hardware.input.InputManager;
import android.os.Binder;
import android.os.IBinder;
@@ -180,23 +178,6 @@
}
@Override
- public Bitmap takeScreenshot(Rect crop, int rotation) {
- synchronized (mLock) {
- throwIfCalledByNotTrustedUidLocked();
- throwIfShutdownLocked();
- throwIfNotConnectedLocked();
- }
- final long identity = Binder.clearCallingIdentity();
- try {
- int width = crop.width();
- int height = crop.height();
- return SurfaceControl.screenshot(crop, width, height, rotation);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- @Override
public boolean clearWindowContentFrameStats(int windowId) throws RemoteException {
synchronized (mLock) {
throwIfCalledByNotTrustedUidLocked();
@@ -449,7 +430,8 @@
info.setCapabilities(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT
| AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
| AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY
- | AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS);
+ | AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS
+ | AccessibilityServiceInfo.CAPABILITY_CAN_TAKE_SCREENSHOT);
try {
// Calling out with a lock held is fine since if the system
// process is gone the client calling in will be killed.
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index caaa686..c584575 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -7418,7 +7418,9 @@
* @param userHandle The user for whom to check the caller-id permission
* @hide
*/
- public boolean getBluetoothContactSharingDisabled(UserHandle userHandle) {
+ @SystemApi
+ @RequiresPermission(permission.INTERACT_ACROSS_USERS)
+ public boolean getBluetoothContactSharingDisabled(@NonNull UserHandle userHandle) {
if (mService != null) {
try {
return mService.getBluetoothContactSharingDisabledForUser(userHandle
diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java
index c8f2ff3..567eb4a 100644
--- a/core/java/android/app/backup/BackupTransport.java
+++ b/core/java/android/app/backup/BackupTransport.java
@@ -85,6 +85,15 @@
public static final int FLAG_NON_INCREMENTAL = 1 << 2;
/**
+ * For key value backup, indicates that the backup contains no new data since the last backup
+ * attempt completed without any errors. The transport should use this to record that
+ * a successful backup attempt has been completed but no backup data has been changed.
+ *
+ * @see #performBackup(PackageInfo, ParcelFileDescriptor, int)
+ */
+ public static final int FLAG_DATA_NOT_CHANGED = 1 << 3;
+
+ /**
* Used as a boolean extra in the binding intent of transports. We pass {@code true} to
* notify transports that the current connection is used for registering the transport.
*/
@@ -302,7 +311,8 @@
* BackupService.doBackup() method. This may be a pipe rather than a file on
* persistent media, so it may not be seekable.
* @param flags a combination of {@link BackupTransport#FLAG_USER_INITIATED}, {@link
- * BackupTransport#FLAG_NON_INCREMENTAL}, {@link BackupTransport#FLAG_INCREMENTAL}, or 0.
+ * BackupTransport#FLAG_NON_INCREMENTAL}, {@link BackupTransport#FLAG_INCREMENTAL},
+ * {@link BackupTransport#FLAG_DATA_NOT_CHANGED},or 0.
* @return one of {@link BackupTransport#TRANSPORT_OK} (OK so far),
* {@link BackupTransport#TRANSPORT_PACKAGE_REJECTED} (to suppress backup of this
* specific package, but allow others to proceed),
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 9ef9574..3860508 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1825,7 +1825,7 @@
* @throws ActivityNotFoundException
* @hide
*/
- @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
@SystemApi
public void startActivityAsUser(@RequiresPermission @NonNull Intent intent,
@NonNull UserHandle user) {
@@ -1873,7 +1873,7 @@
* @throws ActivityNotFoundException
* @hide
*/
- @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
@UnsupportedAppUsage
public void startActivityAsUser(@RequiresPermission Intent intent, @Nullable Bundle options,
UserHandle userId) {
@@ -1979,7 +1979,7 @@
* @see #startActivities(Intent[])
* @see PackageManager#resolveActivity
*/
- @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
public int startActivitiesAsUser(Intent[] intents, Bundle options, UserHandle userHandle) {
throw new RuntimeException("Not implemented. Must override in a subclass.");
}
diff --git a/core/java/android/content/pm/InstallSourceInfo.java b/core/java/android/content/pm/InstallSourceInfo.java
index 4d235f1..c0fdcc9 100644
--- a/core/java/android/content/pm/InstallSourceInfo.java
+++ b/core/java/android/content/pm/InstallSourceInfo.java
@@ -29,32 +29,39 @@
@Nullable private final String mInitiatingPackageName;
+ @Nullable private final SigningInfo mInitiatingPackageSigningInfo;
+
@Nullable private final String mOriginatingPackageName;
@Nullable private final String mInstallingPackageName;
/** @hide */
public InstallSourceInfo(@Nullable String initiatingPackageName,
+ @Nullable SigningInfo initiatingPackageSigningInfo,
@Nullable String originatingPackageName, @Nullable String installingPackageName) {
- this.mInitiatingPackageName = initiatingPackageName;
- this.mOriginatingPackageName = originatingPackageName;
- this.mInstallingPackageName = installingPackageName;
+ mInitiatingPackageName = initiatingPackageName;
+ mInitiatingPackageSigningInfo = initiatingPackageSigningInfo;
+ mOriginatingPackageName = originatingPackageName;
+ mInstallingPackageName = installingPackageName;
}
@Override
public int describeContents() {
- return 0;
+ return mInitiatingPackageSigningInfo == null
+ ? 0 : mInitiatingPackageSigningInfo.describeContents();
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(mInitiatingPackageName);
+ dest.writeParcelable(mInitiatingPackageSigningInfo, flags);
dest.writeString(mOriginatingPackageName);
dest.writeString(mInstallingPackageName);
}
private InstallSourceInfo(Parcel source) {
mInitiatingPackageName = source.readString();
+ mInitiatingPackageSigningInfo = source.readParcelable(SigningInfo.class.getClassLoader());
mOriginatingPackageName = source.readString();
mInstallingPackageName = source.readString();
}
@@ -66,6 +73,14 @@
}
/**
+ * Information about the signing certificates used to sign the initiating package, if available.
+ */
+ @Nullable
+ public SigningInfo getInitiatingPackageSigningInfo() {
+ return mInitiatingPackageSigningInfo;
+ }
+
+ /**
* The name of the package on behalf of which the initiating package requested the installation,
* or null if not available.
* <p>
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 4bfc40e..4f06561 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -6053,6 +6053,11 @@
* If the calling application does not hold the INSTALL_PACKAGES permission then
* the result will always return {@code null} from
* {@link InstallSourceInfo#getOriginatingPackageName()}.
+ * <p>
+ * If the package that requested the install has been uninstalled, then information about it
+ * will only be returned from {@link InstallSourceInfo#getInitiatingPackageName()} and
+ * {@link InstallSourceInfo#getInitiatingPackageSigningInfo()} if the calling package is
+ * requesting its own install information and is not an instant app.
*
* @param packageName The name of the package to query
* @throws NameNotFoundException if the given package name is not installed
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 46559ec..8ba3131 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -662,7 +662,7 @@
* {@hide}
*/
@Deprecated
- @UnsupportedAppUsage
+ @SystemApi
public static final int TYPE_WIFI_P2P = 13;
/**
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index 385cb1d..72a6b39 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -57,9 +57,6 @@
@UnsupportedAppUsage
boolean getRestrictBackground();
- /** Callback used to change internal state on tethering */
- void onTetheringChanged(String iface, boolean tethering);
-
/** Gets the restrict background status based on the caller's UID:
1 - disabled
2 - whitelisted
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index d286204..aae9fd4 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -17,6 +17,8 @@
package android.net;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Build;
@@ -43,9 +45,11 @@
*
* @hide
*/
+@SystemApi
public abstract class NetworkAgent {
- // Guaranteed to be non-null, otherwise registerNetworkAgent() would have thrown
- // an exception. Be careful in tests when mocking though.
+ /**
+ * The {@link Network} corresponding to this object.
+ */
@NonNull
public final Network network;
@@ -58,9 +62,14 @@
private final ArrayList<Message>mPreConnectedQueue = new ArrayList<Message>();
private volatile long mLastBwRefreshTime = 0;
private static final long BW_REFRESH_MIN_WIN_MS = 500;
- private boolean mPollLceScheduled = false;
- private AtomicBoolean mPollLcePending = new AtomicBoolean(false);
- public final int mProviderId;
+ private boolean mBandwidthUpdateScheduled = false;
+ private AtomicBoolean mBandwidthUpdatePending = new AtomicBoolean(false);
+
+ /**
+ * The ID of the {@link NetworkProvider} that created this object, or
+ * {@link NetworkProvider#ID_NONE} if unknown.
+ */
+ public final int providerId;
private static final int BASE = Protocol.BASE_NETWORK_AGENT;
@@ -68,6 +77,7 @@
* Sent by ConnectivityService to the NetworkAgent to inform it of
* suspected connectivity problems on its network. The NetworkAgent
* should take steps to verify and correct connectivity.
+ * @hide
*/
public static final int CMD_SUSPECT_BAD = BASE;
@@ -76,6 +86,7 @@
* ConnectivityService to pass the current NetworkInfo (connection state).
* Sent when the NetworkInfo changes, mainly due to change of state.
* obj = NetworkInfo
+ * @hide
*/
public static final int EVENT_NETWORK_INFO_CHANGED = BASE + 1;
@@ -83,6 +94,7 @@
* Sent by the NetworkAgent to ConnectivityService to pass the current
* NetworkCapabilties.
* obj = NetworkCapabilities
+ * @hide
*/
public static final int EVENT_NETWORK_CAPABILITIES_CHANGED = BASE + 2;
@@ -90,11 +102,14 @@
* Sent by the NetworkAgent to ConnectivityService to pass the current
* NetworkProperties.
* obj = NetworkProperties
+ * @hide
*/
public static final int EVENT_NETWORK_PROPERTIES_CHANGED = BASE + 3;
- /* centralize place where base network score, and network score scaling, will be
+ /**
+ * Centralize the place where base network score, and network score scaling, will be
* stored, so as we can consistently compare apple and oranges, or wifi, ethernet and LTE
+ * @hide
*/
public static final int WIFI_BASE_SCORE = 60;
@@ -102,6 +117,7 @@
* Sent by the NetworkAgent to ConnectivityService to pass the current
* network score.
* obj = network score Integer
+ * @hide
*/
public static final int EVENT_NETWORK_SCORE_CHANGED = BASE + 4;
@@ -114,12 +130,33 @@
* obj = Bundle containing map from {@code REDIRECT_URL_KEY} to {@code String}
* representing URL that Internet probe was redirect to, if it was redirected,
* or mapping to {@code null} otherwise.
+ * @hide
*/
public static final int CMD_REPORT_NETWORK_STATUS = BASE + 7;
+
+ /**
+ * Network validation suceeded.
+ * Corresponds to {@link NetworkCapabilities.NET_CAPABILITY_VALIDATED}.
+ */
+ public static final int VALIDATION_STATUS_VALID = 1;
+
+ /**
+ * Network validation was attempted and failed. This may be received more than once as
+ * subsequent validation attempts are made.
+ */
+ public static final int VALIDATION_STATUS_NOT_VALID = 2;
+
+ // TODO: remove.
+ /** @hide */
public static final int VALID_NETWORK = 1;
+ /** @hide */
public static final int INVALID_NETWORK = 2;
+ /**
+ * The key for the redirect URL in the Bundle argument of {@code CMD_REPORT_NETWORK_STATUS}.
+ * @hide
+ */
public static String REDIRECT_URL_KEY = "redirect URL";
/**
@@ -128,6 +165,7 @@
* CONNECTED so it can be given special treatment at that time.
*
* obj = boolean indicating whether to use this network even if unvalidated
+ * @hide
*/
public static final int EVENT_SET_EXPLICITLY_SELECTED = BASE + 8;
@@ -138,12 +176,14 @@
* responsibility to remember it.
*
* arg1 = 1 if true, 0 if false
+ * @hide
*/
public static final int CMD_SAVE_ACCEPT_UNVALIDATED = BASE + 9;
/**
* Sent by ConnectivityService to the NetworkAgent to inform the agent to pull
* the underlying network connection for updated bandwidth information.
+ * @hide
*/
public static final int CMD_REQUEST_BANDWIDTH_UPDATE = BASE + 10;
@@ -156,6 +196,7 @@
* obj = KeepalivePacketData object describing the data to be sent
*
* Also used internally by ConnectivityService / KeepaliveTracker, with different semantics.
+ * @hide
*/
public static final int CMD_START_SOCKET_KEEPALIVE = BASE + 11;
@@ -165,6 +206,7 @@
* arg1 = slot number of the keepalive to stop.
*
* Also used internally by ConnectivityService / KeepaliveTracker, with different semantics.
+ * @hide
*/
public static final int CMD_STOP_SOCKET_KEEPALIVE = BASE + 12;
@@ -178,6 +220,7 @@
*
* arg1 = slot number of the keepalive
* arg2 = error code
+ * @hide
*/
public static final int EVENT_SOCKET_KEEPALIVE = BASE + 13;
@@ -186,6 +229,7 @@
* that when crossed should trigger a system wakeup and a NetworkCapabilities update.
*
* obj = int[] describing signal strength thresholds.
+ * @hide
*/
public static final int CMD_SET_SIGNAL_STRENGTH_THRESHOLDS = BASE + 14;
@@ -193,6 +237,7 @@
* Sent by ConnectivityService to the NeworkAgent to inform the agent to avoid
* automatically reconnecting to this network (e.g. via autojoin). Happens
* when user selects "No" option on the "Stay connected?" dialog box.
+ * @hide
*/
public static final int CMD_PREVENT_AUTOMATIC_RECONNECT = BASE + 15;
@@ -205,6 +250,7 @@
* This does not happen with UDP, so this message is TCP-specific.
* arg1 = slot number of the keepalive to filter for.
* obj = the keepalive packet to send repeatedly.
+ * @hide
*/
public static final int CMD_ADD_KEEPALIVE_PACKET_FILTER = BASE + 16;
@@ -212,6 +258,7 @@
* Sent by the KeepaliveTracker to NetworkAgent to remove a packet filter. See
* {@link #CMD_ADD_KEEPALIVE_PACKET_FILTER}.
* arg1 = slot number of the keepalive packet filter to remove.
+ * @hide
*/
public static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER = BASE + 17;
@@ -219,27 +266,32 @@
// of dependent changes that would conflict throughout the automerger graph. Having these
// temporarily helps with the process of going through with all these dependent changes across
// the entire tree.
+ /** @hide TODO: decide which of these to expose. */
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score) {
this(looper, context, logTag, ni, nc, lp, score, null, NetworkProvider.ID_NONE);
}
+
+ /** @hide TODO: decide which of these to expose. */
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config) {
this(looper, context, logTag, ni, nc, lp, score, config, NetworkProvider.ID_NONE);
}
+ /** @hide TODO: decide which of these to expose. */
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score, int providerId) {
this(looper, context, logTag, ni, nc, lp, score, null, providerId);
}
+ /** @hide TODO: decide which of these to expose. */
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config,
int providerId) {
mHandler = new NetworkAgentHandler(looper);
LOG_TAG = logTag;
mContext = context;
- mProviderId = providerId;
+ this.providerId = providerId;
if (ni == null || nc == null || lp == null) {
throw new IllegalArgumentException();
}
@@ -287,7 +339,7 @@
case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
if (DBG) log("NetworkAgent channel lost");
// let the client know CS is done with us.
- unwanted();
+ onNetworkUnwanted();
synchronized (mPreConnectedQueue) {
mAsyncChannel = null;
}
@@ -303,16 +355,16 @@
log("CMD_REQUEST_BANDWIDTH_UPDATE request received.");
}
if (currentTimeMs >= (mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS)) {
- mPollLceScheduled = false;
- if (!mPollLcePending.getAndSet(true)) {
- pollLceData();
+ mBandwidthUpdateScheduled = false;
+ if (!mBandwidthUpdatePending.getAndSet(true)) {
+ onBandwidthUpdateRequested();
}
} else {
// deliver the request at a later time rather than discard it completely.
- if (!mPollLceScheduled) {
+ if (!mBandwidthUpdateScheduled) {
long waitTime = mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS
- currentTimeMs + 1;
- mPollLceScheduled = sendEmptyMessageDelayed(
+ mBandwidthUpdateScheduled = sendEmptyMessageDelayed(
CMD_REQUEST_BANDWIDTH_UPDATE, waitTime);
}
}
@@ -325,19 +377,20 @@
+ (msg.arg1 == VALID_NETWORK ? "VALID, " : "INVALID, ")
+ redirectUrl);
}
- networkStatus(msg.arg1, redirectUrl);
+ onValidationStatus(msg.arg1 /* status */, redirectUrl);
break;
}
case CMD_SAVE_ACCEPT_UNVALIDATED: {
- saveAcceptUnvalidated(msg.arg1 != 0);
+ onSaveAcceptUnvalidated(msg.arg1 != 0);
break;
}
case CMD_START_SOCKET_KEEPALIVE: {
- startSocketKeepalive(msg);
+ onStartSocketKeepalive(msg.arg1 /* slot */, msg.arg2 /* interval */,
+ (KeepalivePacketData) msg.obj /* packet */);
break;
}
case CMD_STOP_SOCKET_KEEPALIVE: {
- stopSocketKeepalive(msg);
+ onStopSocketKeepalive(msg.arg1 /* slot */);
break;
}
@@ -350,19 +403,20 @@
for (int i = 0; i < intThresholds.length; i++) {
intThresholds[i] = thresholds.get(i);
}
- setSignalStrengthThresholds(intThresholds);
+ onSignalStrengthThresholdsUpdated(intThresholds);
break;
}
case CMD_PREVENT_AUTOMATIC_RECONNECT: {
- preventAutomaticReconnect();
+ onAutomaticReconnectDisabled();
break;
}
case CMD_ADD_KEEPALIVE_PACKET_FILTER: {
- addKeepalivePacketFilter(msg);
+ onAddKeepalivePacketFilter(msg.arg1 /* slot */,
+ (KeepalivePacketData) msg.obj /* packet */);
break;
}
case CMD_REMOVE_KEEPALIVE_PACKET_FILTER: {
- removeKeepalivePacketFilter(msg);
+ onRemoveKeepalivePacketFilter(msg.arg1 /* slot */);
break;
}
}
@@ -397,14 +451,16 @@
}
/**
- * Called by the bearer code when it has new LinkProperties data.
+ * Must be called by the agent when the network's {@link LinkProperties} change.
+ * @param linkProperties the new LinkProperties.
*/
- public void sendLinkProperties(LinkProperties linkProperties) {
+ public void sendLinkProperties(@NonNull LinkProperties linkProperties) {
queueOrSendMessage(EVENT_NETWORK_PROPERTIES_CHANGED, new LinkProperties(linkProperties));
}
/**
- * Called by the bearer code when it has new NetworkInfo data.
+ * Must be called by the agent when it has a new NetworkInfo object.
+ * @hide TODO: expose something better.
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public void sendNetworkInfo(NetworkInfo networkInfo) {
@@ -412,17 +468,19 @@
}
/**
- * Called by the bearer code when it has new NetworkCapabilities data.
+ * Must be called by the agent when the network's {@link NetworkCapabilities} change.
+ * @param networkCapabilities the new NetworkCapabilities.
*/
- public void sendNetworkCapabilities(NetworkCapabilities networkCapabilities) {
- mPollLcePending.set(false);
+ public void sendNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) {
+ mBandwidthUpdatePending.set(false);
mLastBwRefreshTime = System.currentTimeMillis();
queueOrSendMessage(EVENT_NETWORK_CAPABILITIES_CHANGED,
new NetworkCapabilities(networkCapabilities));
}
/**
- * Called by the bearer code when it has a new score for this network.
+ * Must be called by the agent to update the score of this network.
+ * @param score the new score.
*/
public void sendNetworkScore(int score) {
if (score < 0) {
@@ -434,14 +492,16 @@
}
/**
- * Called by the bearer code when it has a new NetworkScore for this network.
+ * Must be called by the agent when it has a new {@link NetworkScore} for this network.
+ * @param ns the new score.
+ * @hide TODO: unhide the NetworkScore class, and rename to sendNetworkScore.
*/
public void updateScore(@NonNull NetworkScore ns) {
queueOrSendMessage(EVENT_NETWORK_SCORE_CHANGED, new NetworkScore(ns));
}
/**
- * Called by the bearer to indicate this network was manually selected by the user.
+ * Must be called by the agent to indicate this network was manually selected by the user.
* This should be called before the NetworkInfo is marked CONNECTED so that this
* Network can be given special treatment at that time. If {@code acceptUnvalidated} is
* {@code true}, then the system will switch to this network. If it is {@code false} and the
@@ -450,15 +510,16 @@
* {@link #saveAcceptUnvalidated} to persist the user's choice. Thus, if the transport ever
* calls this method with {@code acceptUnvalidated} set to {@code false}, it must also implement
* {@link #saveAcceptUnvalidated} to respect the user's choice.
+ * @hide should move to NetworkAgentConfig.
*/
public void explicitlySelected(boolean acceptUnvalidated) {
explicitlySelected(true /* explicitlySelected */, acceptUnvalidated);
}
/**
- * Called by the bearer to indicate whether the network was manually selected by the user.
- * This should be called before the NetworkInfo is marked CONNECTED so that this
- * Network can be given special treatment at that time.
+ * Must be called by the agent to indicate whether the network was manually selected by the
+ * user. This should be called before the network becomes connected, so it can be given
+ * special treatment when it does.
*
* If {@code explicitlySelected} is {@code true}, and {@code acceptUnvalidated} is {@code true},
* then the system will switch to this network. If {@code explicitlySelected} is {@code true}
@@ -473,6 +534,7 @@
* {@code true}, the system will interpret this as the user having accepted partial connectivity
* on this network. Thus, the system will switch to the network and consider it validated even
* if it only provides partial connectivity, but the network is not otherwise treated specially.
+ * @hide should move to NetworkAgentConfig.
*/
public void explicitlySelected(boolean explicitlySelected, boolean acceptUnvalidated) {
queueOrSendMessage(EVENT_SET_EXPLICITLY_SELECTED,
@@ -486,73 +548,126 @@
* as well, either canceling NetworkRequests or altering their score such that this
* network won't be immediately requested again.
*/
- abstract protected void unwanted();
+ public void onNetworkUnwanted() {
+ unwanted();
+ }
+ /** @hide TODO delete once subclasses have moved to onNetworkUnwanted. */
+ protected void unwanted() {
+ }
/**
* Called when ConnectivityService request a bandwidth update. The parent factory
* shall try to overwrite this method and produce a bandwidth update if capable.
*/
+ public void onBandwidthUpdateRequested() {
+ pollLceData();
+ }
+ /** @hide TODO delete once subclasses have moved to onBandwidthUpdateRequested. */
protected void pollLceData() {
}
/**
* Called when the system determines the usefulness of this network.
*
- * Networks claiming internet connectivity will have their internet
- * connectivity verified.
+ * The system attempts to validate Internet connectivity on networks that provide the
+ * {@link NetworkCapabilities#NET_CAPABILITY_INTERNET} capability.
*
* Currently there are two possible values:
- * {@code VALID_NETWORK} if the system is happy with the connection,
- * {@code INVALID_NETWORK} if the system is not happy.
- * TODO - add indications of captive portal-ness and related success/failure,
- * ie, CAPTIVE_SUCCESS_NETWORK, CAPTIVE_NETWORK for successful login and detection
+ * {@code VALIDATION_STATUS_VALID} if Internet connectivity was validated,
+ * {@code VALIDATION_STATUS_NOT_VALID} if Internet connectivity was not validated.
*
- * This may be called multiple times as the network status changes and may
- * generate false negatives if we lose ip connectivity before the link is torn down.
+ * This may be called multiple times as network status changes, or if there are multiple
+ * subsequent attempts to validate connectivity that fail.
*
- * @param status one of {@code VALID_NETWORK} or {@code INVALID_NETWORK}.
- * @param redirectUrl If the Internet probe was redirected, this is the destination it was
- * redirected to, otherwise {@code null}.
+ * @param status one of {@code VALIDATION_STATUS_VALID} or {@code VALIDATION_STATUS_NOT_VALID}.
+ * @param redirectUrl If Internet connectivity is being redirected (e.g., on a captive portal),
+ * this is the destination the probes are being redirected to, otherwise {@code null}.
*/
+ public void onValidationStatus(int status, @Nullable String redirectUrl) {
+ networkStatus(status, redirectUrl);
+ }
+ /** @hide TODO delete once subclasses have moved to onValidationStatus */
protected void networkStatus(int status, String redirectUrl) {
}
+
/**
* Called when the user asks to remember the choice to use this network even if unvalidated.
* The transport is responsible for remembering the choice, and the next time the user connects
* to the network, should explicitlySelected with {@code acceptUnvalidated} set to {@code true}.
* This method will only be called if {@link #explicitlySelected} was called with
* {@code acceptUnvalidated} set to {@code false}.
+ * @param accept whether the user wants to use the network even if unvalidated.
*/
+ public void onSaveAcceptUnvalidated(boolean accept) {
+ saveAcceptUnvalidated(accept);
+ }
+ /** @hide TODO delete once subclasses have moved to onSaveAcceptUnvalidated */
protected void saveAcceptUnvalidated(boolean accept) {
}
/**
* Requests that the network hardware send the specified packet at the specified interval.
+ *
+ * @param slot the hardware slot on which to start the keepalive.
+ * @param intervalSeconds the interval between packets
+ * @param packet the packet to send.
*/
+ public void onStartSocketKeepalive(int slot, int intervalSeconds,
+ @NonNull KeepalivePacketData packet) {
+ Message msg = mHandler.obtainMessage(CMD_START_SOCKET_KEEPALIVE, slot, intervalSeconds,
+ packet);
+ startSocketKeepalive(msg);
+ msg.recycle();
+ }
+ /** @hide TODO delete once subclasses have moved to onStartSocketKeepalive */
protected void startSocketKeepalive(Message msg) {
onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_UNSUPPORTED);
}
/**
- * Requests that the network hardware send the specified packet at the specified interval.
+ * Requests that the network hardware stop a previously-started keepalive.
+ *
+ * @param slot the hardware slot on which to stop the keepalive.
*/
+ public void onStopSocketKeepalive(int slot) {
+ Message msg = mHandler.obtainMessage(CMD_STOP_SOCKET_KEEPALIVE, slot, 0, null);
+ stopSocketKeepalive(msg);
+ msg.recycle();
+ }
+ /** @hide TODO delete once subclasses have moved to onStopSocketKeepalive */
protected void stopSocketKeepalive(Message msg) {
onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_UNSUPPORTED);
}
/**
- * Called by the network when a socket keepalive event occurs.
+ * Must be called by the agent when a socket keepalive event occurs.
+ *
+ * @param slot the hardware slot on which the event occurred.
+ * @param event the event that occurred.
*/
+ public void sendSocketKeepaliveEvent(int slot, int event) {
+ queueOrSendMessage(EVENT_SOCKET_KEEPALIVE, slot, event);
+ }
+ /** @hide TODO delete once callers have moved to sendSocketKeepaliveEvent */
public void onSocketKeepaliveEvent(int slot, int reason) {
- queueOrSendMessage(EVENT_SOCKET_KEEPALIVE, slot, reason);
+ sendSocketKeepaliveEvent(slot, reason);
}
/**
* Called by ConnectivityService to add specific packet filter to network hardware to block
- * ACKs matching the sent keepalive packets. Implementations that support this feature must
- * override this method.
+ * replies (e.g., TCP ACKs) matching the sent keepalive packets. Implementations that support
+ * this feature must override this method.
+ *
+ * @param slot the hardware slot on which the keepalive should be sent.
+ * @param packet the packet that is being sent.
*/
+ public void onAddKeepalivePacketFilter(int slot, @NonNull KeepalivePacketData packet) {
+ Message msg = mHandler.obtainMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER, slot, 0, packet);
+ addKeepalivePacketFilter(msg);
+ msg.recycle();
+ }
+ /** @hide TODO delete once subclasses have moved to onAddKeepalivePacketFilter */
protected void addKeepalivePacketFilter(Message msg) {
}
@@ -560,14 +675,28 @@
* Called by ConnectivityService to remove a packet filter installed with
* {@link #addKeepalivePacketFilter(Message)}. Implementations that support this feature
* must override this method.
+ *
+ * @param slot the hardware slot on which the keepalive is being sent.
*/
+ public void onRemoveKeepalivePacketFilter(int slot) {
+ Message msg = mHandler.obtainMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER, slot, 0, null);
+ removeKeepalivePacketFilter(msg);
+ msg.recycle();
+ }
+ /** @hide TODO delete once subclasses have moved to onRemoveKeepalivePacketFilter */
protected void removeKeepalivePacketFilter(Message msg) {
}
/**
* Called by ConnectivityService to inform this network transport of signal strength thresholds
* that when crossed should trigger a system wakeup and a NetworkCapabilities update.
+ *
+ * @param thresholds the array of thresholds that should trigger wakeups.
*/
+ public void onSignalStrengthThresholdsUpdated(@NonNull int[] thresholds) {
+ setSignalStrengthThresholds(thresholds);
+ }
+ /** @hide TODO delete once subclasses have moved to onSetSignalStrengthThresholds */
protected void setSignalStrengthThresholds(int[] thresholds) {
}
@@ -577,9 +706,14 @@
* responsible for making sure the device does not automatically reconnect to the same network
* after the {@code unwanted} call.
*/
+ public void onAutomaticReconnectDisabled() {
+ preventAutomaticReconnect();
+ }
+ /** @hide TODO delete once subclasses have moved to onAutomaticReconnectDisabled */
protected void preventAutomaticReconnect() {
}
+ /** @hide */
protected void log(String s) {
Log.d(LOG_TAG, "NetworkAgent: " + s);
}
diff --git a/core/java/android/os/IVibratorService.aidl b/core/java/android/os/IVibratorService.aidl
index 7b2d148..416d692 100644
--- a/core/java/android/os/IVibratorService.aidl
+++ b/core/java/android/os/IVibratorService.aidl
@@ -24,7 +24,7 @@
{
boolean hasVibrator();
boolean hasAmplitudeControl();
- boolean setAlwaysOnEffect(int id, in VibrationEffect effect,
+ boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, in VibrationEffect effect,
in VibrationAttributes attributes);
void vibrate(int uid, String opPkg, in VibrationEffect effect,
in VibrationAttributes attributes, String reason, IBinder token);
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
index c1542c7..8050454 100644
--- a/core/java/android/os/SystemVibrator.java
+++ b/core/java/android/os/SystemVibrator.java
@@ -70,14 +70,15 @@
}
@Override
- public boolean setAlwaysOnEffect(int id, VibrationEffect effect, AudioAttributes attributes) {
+ public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, VibrationEffect effect,
+ AudioAttributes attributes) {
if (mService == null) {
Log.w(TAG, "Failed to set always-on effect; no vibrator service.");
return false;
}
try {
VibrationAttributes atr = new VibrationAttributes.Builder(attributes, effect).build();
- return mService.setAlwaysOnEffect(id, effect, atr);
+ return mService.setAlwaysOnEffect(uid, opPkg, alwaysOnId, effect, atr);
} catch (RemoteException e) {
Log.w(TAG, "Failed to set always-on effect.", e);
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index d8fadfb..f84e483 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -192,7 +192,11 @@
/**
* Specifies if a user is disallowed from changing Wi-Fi
* access points. The default value is <code>false</code>.
- * <p>This restriction has no effect in a managed profile.
+ * <p>
+ * Device owner and profile owner can set this restriction, although the restriction has no
+ * effect in a managed profile. When it is set by the profile owner of an organization-owned
+ * managed profile on the parent profile, it will disallow the personal user from changing
+ * Wi-Fi access points.
*
* <p>Key for user restrictions.
* <p>Type: Boolean
@@ -242,8 +246,13 @@
/**
* Specifies if a user is disallowed from turning on location sharing.
* The default value is <code>false</code>.
- * <p>In a managed profile, location sharing always reflects the primary user's setting, but
+ * <p>
+ * In a managed profile, location sharing always reflects the primary user's setting, but
* can be overridden and forced off by setting this restriction to true in the managed profile.
+ * <p>
+ * Device owner and profile owner can set this restriction. When it is set by the profile
+ * owner of an organization-owned managed profile on the parent profile, it will prevent the
+ * user from turning on location sharing in the personal profile.
*
* <p>Key for user restrictions.
* <p>Type: Boolean
@@ -349,9 +358,14 @@
* Specifies if a user is disallowed from configuring bluetooth.
* This does <em>not</em> restrict the user from turning bluetooth on or off.
* The default value is <code>false</code>.
- * <p>This restriction doesn't prevent the user from using bluetooth. For disallowing usage of
+ * <p>
+ * This restriction doesn't prevent the user from using bluetooth. For disallowing usage of
* bluetooth completely on the device, use {@link #DISALLOW_BLUETOOTH}.
- * <p>This restriction has no effect in a managed profile.
+ * <p>
+ * Device owner and profile owner can set this restriction, although the restriction has no
+ * effect in a managed profile. When it is set by the profile owner of an organization-owned
+ * managed profile on the parent profile, it will disallow the personal user from configuring
+ * bluetooth.
*
* <p>Key for user restrictions.
* <p>Type: Boolean
@@ -364,8 +378,10 @@
/**
* Specifies if bluetooth is disallowed on the device.
*
- * <p> This restriction can only be set by the device owner and the profile owner on the
- * primary user and it applies globally - i.e. it disables bluetooth on the entire device.
+ * <p> This restriction can only be set by the device owner, the profile owner on the
+ * primary user or the profile owner of an organization-owned managed profile on the
+ * parent profile and it applies globally - i.e. it disables bluetooth on the entire
+ * device.
* <p>The default value is <code>false</code>.
* <p>Key for user restrictions.
* <p>Type: Boolean
@@ -377,8 +393,9 @@
/**
* Specifies if outgoing bluetooth sharing is disallowed on the device. Device owner and profile
- * owner can set this restriction. When it is set by device owner, all users on this device will
- * be affected.
+ * owner can set this restriction. When it is set by device owner or the profile owner of an
+ * organization-owned managed profile on the parent profile, all users on this device will be
+ * affected.
*
* <p>Default is <code>true</code> for managed profiles and false for otherwise. When a device
* upgrades to {@link android.os.Build.VERSION_CODES#O}, the system sets it for all existing
@@ -394,7 +411,8 @@
/**
* Specifies if a user is disallowed from transferring files over
- * USB. This can only be set by device owners and profile owners on the primary user.
+ * USB. This can only be set by device owners, profile owners on the primary user or
+ * profile owners of organization-owned managed profiles on the parent profile.
* The default value is <code>false</code>.
*
* <p>Key for user restrictions.
@@ -453,8 +471,9 @@
/**
* Specifies if a user is disallowed from enabling or accessing debugging features. When set on
- * the primary user, disables debugging features altogether, including USB debugging. When set
- * on a managed profile or a secondary user, blocks debugging for that user only, including
+ * the primary user or by the profile owner of an organization-owned managed profile on the
+ * parent profile, disables debugging features altogether, including USB debugging. When set on
+ * a managed profile or a secondary user, blocks debugging for that user only, including
* starting activities, making service calls, accessing content providers, sending broadcasts,
* installing/uninstalling packages, clearing user data, etc.
* The default value is <code>false</code>.
@@ -485,18 +504,19 @@
/**
* Specifies if a user is disallowed from enabling or disabling location providers. As a
- * result, user is disallowed from turning on or off location. Device owner and profile owners
- * can set this restriction and it only applies on the managed user.
+ * result, user is disallowed from turning on or off location.
*
- * <p>In a managed profile, location sharing is forced off when it's off on primary user, so
- * user can still turn off location sharing on managed profile when the restriction is set by
- * profile owner on managed profile.
- *
- * <p>This user restriction is different from {@link #DISALLOW_SHARE_LOCATION},
+ * <p>
+ * In a managed profile, location sharing is forced off when it is turned off on the primary
+ * user or by the profile owner of an organization-owned managed profile on the parent profile.
+ * The user can still turn off location sharing on a managed profile when the restriction is
+ * set by the profile owner on a managed profile.
+ * <p>
+ * This user restriction is different from {@link #DISALLOW_SHARE_LOCATION},
* as the device owner or profile owner can still enable or disable location mode via
* {@link DevicePolicyManager#setLocationEnabled} when this restriction is on.
- *
- * <p>The default value is <code>false</code>.
+ * <p>
+ * The default value is <code>false</code>.
*
* <p>Key for user restrictions.
* <p>Type: Boolean
@@ -510,7 +530,8 @@
/**
* Specifies if date, time and timezone configuring is disallowed.
*
- * <p>When restriction is set by device owners, it applies globally - i.e., it disables date,
+ * <p>When restriction is set by device owners or profile owners of organization-owned
+ * managed profiles on the parent profile, it applies globally - i.e., it disables date,
* time and timezone setting on the entire device and all users will be affected. When it's set
* by profile owners, it's only applied to the managed user.
* <p>The default value is <code>false</code>.
@@ -526,8 +547,9 @@
/**
* Specifies if a user is disallowed from configuring Tethering
- * & portable hotspots. This can only be set by device owners and profile owners on the
- * primary user. The default value is <code>false</code>.
+ * & portable hotspots. This can only be set by device owners, profile owners on the
+ * primary user or profile owners of organization-owned managed profiles on the parent profile.
+ * The default value is <code>false</code>.
* <p>In Android 9.0 or higher, if tethering is enabled when this restriction is set,
* tethering will be automatically turned off.
*
@@ -571,8 +593,8 @@
/**
* Specifies if a user is disallowed from adding new users. This can only be set by device
- * owners and profile owners on the primary user.
- * The default value is <code>false</code>.
+ * owners, profile owners on the primary user or profile owners of organization-owned managed
+ * profiles on the parent profile. The default value is <code>false</code>.
* <p>This restriction has no effect on secondary users and managed profiles since only the
* primary user can add other users.
*
@@ -621,7 +643,8 @@
/**
* Specifies if a user is disallowed from configuring cell
- * broadcasts. This can only be set by device owners and profile owners on the primary user.
+ * broadcasts. This can only be set by device owners, profile owners on the primary user or
+ * profile owners of organization-owned managed profiles on the parent profile.
* The default value is <code>false</code>.
* <p>This restriction has no effect on secondary users and managed profiles since only the
* primary user can configure cell broadcasts.
@@ -636,7 +659,8 @@
/**
* Specifies if a user is disallowed from configuring mobile
- * networks. This can only be set by device owners and profile owners on the primary user.
+ * networks. This can only be set by device owners, profile owners on the primary user or
+ * profile owners of organization-owned managed profiles on the parent profile.
* The default value is <code>false</code>.
* <p>This restriction has no effect on secondary users and managed profiles since only the
* primary user can configure mobile networks.
@@ -739,6 +763,10 @@
/**
* Specifies that the user is not allowed to send or receive
* SMS messages. The default value is <code>false</code>.
+ * <p>
+ * Device owner and profile owner can set this restriction. When it is set by the
+ * profile owner of an organization-owned managed profile on the parent profile,
+ * it will disable SMS in the personal profile.
*
* <p>Key for user restrictions.
* <p>Type: Boolean
@@ -857,7 +885,8 @@
/**
* Specifies if the user is not allowed to reboot the device into safe boot mode.
- * This can only be set by device owners and profile owners on the primary user.
+ * This can only be set by device owners, profile owners on the primary user or profile
+ * owners of organization-owned managed profiles on the parent profile.
* The default value is <code>false</code>.
*
* <p>Key for user restrictions.
@@ -896,6 +925,12 @@
/**
* Specifies if a user is not allowed to use the camera.
+ * <p>
+ * Device owner and profile owner can set this restriction. When the restriction is set by
+ * the device owner or the profile owner of an organization-owned managed profile on the
+ * parent profile, it is applied globally.
+ * <p>
+ * The default value is <code>false</code>.
*
* @see DevicePolicyManager#addUserRestriction(ComponentName, String)
* @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
@@ -916,7 +951,8 @@
/**
* Specifies if a user is not allowed to use cellular data when roaming. This can only be set by
- * device owners. The default value is <code>false</code>.
+ * device owners or profile owners of organization-owned managed profiles on the parent profile.
+ * The default value is <code>false</code>.
*
* @see DevicePolicyManager#addUserRestriction(ComponentName, String)
* @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
@@ -1011,8 +1047,9 @@
* Specifies if the contents of a user's screen is not allowed to be captured for artificial
* intelligence purposes.
*
- * <p>Device owner and profile owner can set this restriction. When it is set by device owner,
- * only the target user will be affected.
+ * <p>Device owner and profile owner can set this restriction. When it is set by the
+ * device owner or the profile owner of an organization-owned managed profile on the parent
+ * profile, only the target user will be affected.
*
* <p>The default value is <code>false</code>.
*
@@ -1026,8 +1063,9 @@
* Specifies if the current user is able to receive content suggestions for selections based on
* the contents of their screen.
*
- * <p>Device owner and profile owner can set this restriction. When it is set by device owner,
- * only the target user will be affected.
+ * <p>Device owner and profile owner can set this restriction. When it is set by the
+ * device owner or the profile owner of an organization-owned managed profile on the parent
+ * profile, only the target user will be affected.
*
* <p>The default value is <code>false</code>.
*
@@ -1093,7 +1131,9 @@
*
* <p>The default value is <code>false</code>.
*
- * <p>This user restriction can only be applied by the Device Owner.
+ * <p>This user restriction can only be applied by the device owner or the profile owner
+ * of an organization-owned managed profile on the parent profile.
+ *
* <p>Key for user restrictions.
* <p>Type: Boolean
* @see DevicePolicyManager#addUserRestriction(ComponentName, String)
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index ccbb0f1..ae75f3d 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -155,7 +155,7 @@
/**
* Configure an always-on haptics effect.
*
- * @param id The board-specific always-on ID to configure.
+ * @param alwaysOnId The board-specific always-on ID to configure.
* @param effect Vibration effect to assign to always-on id. Passing null will disable it.
* @param attributes {@link AudioAttributes} corresponding to the vibration. For example,
* specify {@link AudioAttributes#USAGE_ALARM} for alarm vibrations or
@@ -164,8 +164,17 @@
* @hide
*/
@RequiresPermission(android.Manifest.permission.VIBRATE_ALWAYS_ON)
- public boolean setAlwaysOnEffect(int id, @Nullable VibrationEffect effect,
- @Nullable AudioAttributes attributes) {
+ public boolean setAlwaysOnEffect(int alwaysOnId, @Nullable VibrationEffect effect,
+ @Nullable AudioAttributes attributes) {
+ return setAlwaysOnEffect(Process.myUid(), mPackageName, alwaysOnId, effect, attributes);
+ }
+
+ /**
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.VIBRATE_ALWAYS_ON)
+ public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId,
+ @Nullable VibrationEffect effect, @Nullable AudioAttributes attributes) {
Log.w(TAG, "Always-on effects aren't supported");
return false;
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 089122d..e2b33e0 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6420,6 +6420,15 @@
"accessibility_button_target_component";
/**
+ * The system class name of magnification controller which is a target to be toggled via
+ * accessibility shortcut or accessibility button.
+ *
+ * @hide
+ */
+ public static final String ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER =
+ "com.android.server.accessibility.MagnificationController";
+
+ /**
* If touch exploration is enabled.
*/
public static final String TOUCH_EXPLORATION_ENABLED = "touch_exploration_enabled";
diff --git a/core/java/android/timezone/CountryTimeZones.java b/core/java/android/timezone/CountryTimeZones.java
new file mode 100644
index 0000000..ada59d6
--- /dev/null
+++ b/core/java/android/timezone/CountryTimeZones.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.timezone;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.icu.util.TimeZone;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Information about a country's time zones.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class CountryTimeZones {
+
+ /**
+ * A mapping to a time zone ID with some associated metadata.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final class TimeZoneMapping {
+
+ private libcore.timezone.CountryTimeZones.TimeZoneMapping mDelegate;
+
+ TimeZoneMapping(libcore.timezone.CountryTimeZones.TimeZoneMapping delegate) {
+ this.mDelegate = Objects.requireNonNull(delegate);
+ }
+
+ /**
+ * Returns the ID for this mapping. See also {@link #getTimeZone()} which handles when the
+ * ID is unrecognized.
+ */
+ @NonNull
+ public String getTimeZoneId() {
+ return mDelegate.timeZoneId;
+ }
+
+ /**
+ * Returns a {@link TimeZone} object for this mapping, or {@code null} if the ID is
+ * unrecognized.
+ */
+ @Nullable
+ public TimeZone getTimeZone() {
+ return mDelegate.getTimeZone();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ TimeZoneMapping that = (TimeZoneMapping) o;
+ return this.mDelegate.equals(that.mDelegate);
+ }
+
+ @Override
+ public int hashCode() {
+ return this.mDelegate.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return mDelegate.toString();
+ }
+ }
+
+ /**
+ * The result of lookup up a time zone using offset information (and possibly more).
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final class OffsetResult {
+
+ private final TimeZone mTimeZone;
+ private final boolean mIsOnlyMatch;
+
+ /** Creates an instance with the supplied information. */
+ public OffsetResult(@NonNull TimeZone timeZone, boolean isOnlyMatch) {
+ mTimeZone = Objects.requireNonNull(timeZone);
+ mIsOnlyMatch = isOnlyMatch;
+ }
+
+ /**
+ * Returns a time zone that matches the supplied criteria.
+ */
+ @NonNull
+ public TimeZone getTimeZone() {
+ return mTimeZone;
+ }
+
+ /**
+ * Returns {@code true} if there is only one matching time zone for the supplied criteria.
+ */
+ public boolean isOnlyMatch() {
+ return mIsOnlyMatch;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ OffsetResult that = (OffsetResult) o;
+ return mIsOnlyMatch == that.mIsOnlyMatch
+ && mTimeZone.getID().equals(that.mTimeZone.getID());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mTimeZone, mIsOnlyMatch);
+ }
+
+ @Override
+ public String toString() {
+ return "OffsetResult{"
+ + "mTimeZone=" + mTimeZone
+ + ", mIsOnlyMatch=" + mIsOnlyMatch
+ + '}';
+ }
+ }
+
+ @NonNull
+ private final libcore.timezone.CountryTimeZones mDelegate;
+
+ CountryTimeZones(libcore.timezone.CountryTimeZones delegate) {
+ mDelegate = delegate;
+ }
+
+ /**
+ * Returns true if the ISO code for the country is a match for the one specified.
+ */
+ public boolean isForCountryCode(@NonNull String countryIso) {
+ return mDelegate.isForCountryCode(countryIso);
+ }
+
+ /**
+ * Returns the default time zone ID for the country. Can return {@code null} in cases when no
+ * data is available or the time zone ID was not recognized.
+ */
+ @Nullable
+ public String getDefaultTimeZoneId() {
+ return mDelegate.getDefaultTimeZoneId();
+ }
+
+ /**
+ * Returns the default time zone for the country. Can return {@code null} in cases when no data
+ * is available or the time zone ID was not recognized.
+ */
+ @Nullable
+ public TimeZone getDefaultTimeZone() {
+ return mDelegate.getDefaultTimeZone();
+ }
+
+ /**
+ * Qualifier for a country's default time zone. {@code true} indicates whether the default
+ * would be a good choice <em>generally</em> when there's no other information available.
+ */
+ public boolean isDefaultTimeZoneBoosted() {
+ return mDelegate.getDefaultTimeZoneBoost();
+ }
+
+ /**
+ * Returns true if the country has at least one zone that is the same as UTC at the given time.
+ */
+ public boolean hasUtcZone(long whenMillis) {
+ return mDelegate.hasUtcZone(whenMillis);
+ }
+
+ /**
+ * Returns a time zone for the country, if there is one, that matches the desired properties. If
+ * there are multiple matches and the {@code bias} is one of them then it is returned, otherwise
+ * an arbitrary match is returned based on the {@link #getEffectiveTimeZoneMappingsAt(long)}
+ * ordering.
+ *
+ * @param totalOffsetMillis the offset from UTC at {@code whenMillis}
+ * @param isDst the Daylight Savings Time state at {@code whenMillis}. {@code true} means DST,
+ * {@code false} means not DST, {@code null} means unknown
+ * @param dstOffsetMillis the part of {@code totalOffsetMillis} contributed by DST, only used if
+ * {@code isDst} is {@code true}. The value can be {@code null} if the DST offset is
+ * unknown
+ * @param whenMillis the UTC time to match against
+ * @param bias the time zone to prefer, can be {@code null}
+ */
+ @Nullable
+ public OffsetResult lookupByOffsetWithBias(int totalOffsetMillis, @Nullable Boolean isDst,
+ @SuppressLint("AutoBoxing") @Nullable Integer dstOffsetMillis, long whenMillis,
+ @Nullable TimeZone bias) {
+ libcore.timezone.CountryTimeZones.OffsetResult delegateOffsetResult =
+ mDelegate.lookupByOffsetWithBias(
+ totalOffsetMillis, isDst, dstOffsetMillis, whenMillis, bias);
+ return delegateOffsetResult == null ? null :
+ new OffsetResult(delegateOffsetResult.mTimeZone, delegateOffsetResult.mOneMatch);
+ }
+
+ /**
+ * Returns an immutable, ordered list of time zone mappings for the country in an undefined but
+ * "priority" order, filtered so that only "effective" time zone IDs are returned. An
+ * "effective" time zone is one that differs from another time zone used in the country after
+ * {@code whenMillis}. The list can be empty if there were no zones configured or the configured
+ * zone IDs were not recognized.
+ */
+ @NonNull
+ public List<TimeZoneMapping> getEffectiveTimeZoneMappingsAt(long whenMillis) {
+ List<libcore.timezone.CountryTimeZones.TimeZoneMapping> delegateList =
+ mDelegate.getEffectiveTimeZoneMappingsAt(whenMillis);
+
+ List<TimeZoneMapping> toReturn = new ArrayList<>(delegateList.size());
+ for (libcore.timezone.CountryTimeZones.TimeZoneMapping delegateMapping : delegateList) {
+ toReturn.add(new TimeZoneMapping(delegateMapping));
+ }
+ return Collections.unmodifiableList(toReturn);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ CountryTimeZones that = (CountryTimeZones) o;
+ return mDelegate.equals(that.mDelegate);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mDelegate);
+ }
+
+ @Override
+ public String toString() {
+ return mDelegate.toString();
+ }
+}
diff --git a/core/java/android/timezone/TelephonyLookup.java b/core/java/android/timezone/TelephonyLookup.java
new file mode 100644
index 0000000..39dbe85
--- /dev/null
+++ b/core/java/android/timezone/TelephonyLookup.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.timezone;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.Objects;
+
+/**
+ * A class that can find time zone-related information about telephony networks.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public class TelephonyLookup {
+
+ private static Object sLock = new Object();
+ @GuardedBy("sLock")
+ private static TelephonyLookup sInstance;
+
+ @NonNull
+ private final libcore.timezone.TelephonyLookup mDelegate;
+
+ /**
+ * Obtains an instance for use when resolving telephony time zone information. This method never
+ * returns {@code null}.
+ */
+ @NonNull
+ public static TelephonyLookup getInstance() {
+ synchronized (sLock) {
+ if (sInstance == null) {
+ sInstance = new TelephonyLookup(libcore.timezone.TelephonyLookup.getInstance());
+ }
+ return sInstance;
+ }
+ }
+
+ private TelephonyLookup(@NonNull libcore.timezone.TelephonyLookup delegate) {
+ mDelegate = Objects.requireNonNull(delegate);
+ }
+
+ /**
+ * Returns an object capable of querying telephony network information. This method can return
+ * {@code null} in the event of an error while reading the underlying data files.
+ */
+ @Nullable
+ public TelephonyNetworkFinder getTelephonyNetworkFinder() {
+ libcore.timezone.TelephonyNetworkFinder telephonyNetworkFinderDelegate =
+ mDelegate.getTelephonyNetworkFinder();
+ return telephonyNetworkFinderDelegate != null
+ ? new TelephonyNetworkFinder(telephonyNetworkFinderDelegate) : null;
+ }
+}
diff --git a/core/java/android/timezone/TelephonyNetwork.java b/core/java/android/timezone/TelephonyNetwork.java
new file mode 100644
index 0000000..ae39fbd
--- /dev/null
+++ b/core/java/android/timezone/TelephonyNetwork.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.timezone;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import java.util.Objects;
+
+/**
+ * Information about a telephony network.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public class TelephonyNetwork {
+
+ @NonNull
+ private final libcore.timezone.TelephonyNetwork mDelegate;
+
+ TelephonyNetwork(@NonNull libcore.timezone.TelephonyNetwork delegate) {
+ mDelegate = Objects.requireNonNull(delegate);
+ }
+
+ /**
+ * Returns the Mobile Country Code of the network.
+ */
+ @NonNull
+ public String getMcc() {
+ return mDelegate.getMcc();
+ }
+
+ /**
+ * Returns the Mobile Network Code of the network.
+ */
+ @NonNull
+ public String getMnc() {
+ return mDelegate.getMnc();
+ }
+
+ /**
+ * Returns the country in which the network operates as an ISO 3166 alpha-2 (lower case).
+ */
+ @NonNull
+ public String getCountryIsoCode() {
+ return mDelegate.getCountryIsoCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ TelephonyNetwork that = (TelephonyNetwork) o;
+ return mDelegate.equals(that.mDelegate);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mDelegate);
+ }
+
+ @Override
+ public String toString() {
+ return "TelephonyNetwork{"
+ + "mDelegate=" + mDelegate
+ + '}';
+ }
+}
diff --git a/core/java/android/timezone/TelephonyNetworkFinder.java b/core/java/android/timezone/TelephonyNetworkFinder.java
new file mode 100644
index 0000000..a81a516
--- /dev/null
+++ b/core/java/android/timezone/TelephonyNetworkFinder.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.timezone;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+
+import java.util.Objects;
+
+/**
+ * A class that can find telephony networks loaded via {@link TelephonyLookup}.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public class TelephonyNetworkFinder {
+
+ @NonNull
+ private final libcore.timezone.TelephonyNetworkFinder mDelegate;
+
+ TelephonyNetworkFinder(libcore.timezone.TelephonyNetworkFinder delegate) {
+ mDelegate = Objects.requireNonNull(delegate);
+ }
+
+ /**
+ * Returns information held about a specific MCC + MNC combination. It is expected for this
+ * method to return {@code null}. Only known, unusual networks will typically have information
+ * returned, e.g. if they operate in countries other than the one suggested by their MCC.
+ */
+ @Nullable
+ public TelephonyNetwork findNetworkByMccMnc(@NonNull String mcc, @NonNull String mnc) {
+ Objects.requireNonNull(mcc);
+ Objects.requireNonNull(mnc);
+
+ libcore.timezone.TelephonyNetwork telephonyNetworkDelegate =
+ mDelegate.findNetworkByMccMnc(mcc, mnc);
+ return telephonyNetworkDelegate != null
+ ? new TelephonyNetwork(telephonyNetworkDelegate) : null;
+ }
+}
diff --git a/core/java/android/timezone/TimeZoneFinder.java b/core/java/android/timezone/TimeZoneFinder.java
new file mode 100644
index 0000000..15dfe62
--- /dev/null
+++ b/core/java/android/timezone/TimeZoneFinder.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.timezone;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * A class that can be used to find time zones.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class TimeZoneFinder {
+
+ private static Object sLock = new Object();
+ @GuardedBy("sLock")
+ private static TimeZoneFinder sInstance;
+
+ private final libcore.timezone.TimeZoneFinder mDelegate;
+
+ private TimeZoneFinder(libcore.timezone.TimeZoneFinder delegate) {
+ mDelegate = delegate;
+ }
+
+ /**
+ * Obtains an instance for use when resolving telephony time zone information. This method never
+ * returns {@code null}.
+ */
+ @NonNull
+ public static TimeZoneFinder getInstance() {
+ synchronized (sLock) {
+ if (sInstance == null) {
+ sInstance = new TimeZoneFinder(libcore.timezone.TimeZoneFinder.getInstance());
+ }
+ }
+ return sInstance;
+ }
+
+ /**
+ * Returns a {@link CountryTimeZones} object associated with the specified country code.
+ * Caching is handled as needed. If the country code is not recognized or there is an error
+ * during lookup this method can return null.
+ */
+ @Nullable
+ public CountryTimeZones lookupCountryTimeZones(@NonNull String countryIso) {
+ libcore.timezone.CountryTimeZones delegate = mDelegate.lookupCountryTimeZones(countryIso);
+ return delegate == null ? null : new CountryTimeZones(delegate);
+ }
+}
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index b4c8795..b9f08ad 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
+import android.graphics.Bitmap;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -828,6 +829,31 @@
}
/**
+ * Takes the screenshot of the specified display and returns it by bitmap format.
+ *
+ * @param connectionId The id of a connection for interacting with the system.
+ * @param displayId The logic display id, use {@link Display#DEFAULT_DISPLAY} for
+ * default display.
+ * @return The screenshot bitmap on success, null otherwise.
+ */
+ public Bitmap takeScreenshot(int connectionId, int displayId) {
+ Bitmap screenShot = null;
+ try {
+ IAccessibilityServiceConnection connection = getConnection(connectionId);
+ if (connection != null) {
+ screenShot = connection.takeScreenshot(displayId);
+ } else {
+ if (DEBUG) {
+ Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
+ }
+ }
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Error while calling remote takeScreenshot", re);
+ }
+ return screenShot;
+ }
+
+ /**
* Clears the result state.
*/
private void clearResultLocked() {
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 2e5a4b5..3dfeffb 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -1195,6 +1195,19 @@
@TestApi
@RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
public void performAccessibilityShortcut() {
+ performAccessibilityShortcut(null);
+ }
+
+ /**
+ * Perform the accessibility shortcut for the given target which is assigned to the shortcut.
+ *
+ * @param targetName The flattened {@link ComponentName} string or the class name of a system
+ * class implementing a supported accessibility feature, or {@code null} if there's no
+ * specified target.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
+ public void performAccessibilityShortcut(@Nullable String targetName) {
final IAccessibilityManager service;
synchronized (mLock) {
service = getServiceLocked();
@@ -1203,7 +1216,7 @@
}
}
try {
- service.performAccessibilityShortcut();
+ service.performAccessibilityShortcut(targetName);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error performing accessibility shortcut. ", re);
}
@@ -1270,7 +1283,22 @@
* @param displayId The logical display id.
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE)
public void notifyAccessibilityButtonClicked(int displayId) {
+ notifyAccessibilityButtonClicked(displayId, null);
+ }
+
+ /**
+ * Perform the accessibility button for the given target which is assigned to the button.
+ *
+ * @param displayId displayId The logical display id.
+ * @param targetName The flattened {@link ComponentName} string or the class name of a system
+ * class implementing a supported accessibility feature, or {@code null} if there's no
+ * specified target.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE)
+ public void notifyAccessibilityButtonClicked(int displayId, @Nullable String targetName) {
final IAccessibilityManager service;
synchronized (mLock) {
service = getServiceLocked();
@@ -1279,7 +1307,7 @@
}
}
try {
- service.notifyAccessibilityButtonClicked(displayId);
+ service.notifyAccessibilityButtonClicked(displayId, targetName);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error while dispatching accessibility button click", re);
}
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 392db57..fcaaa2e 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -66,12 +66,12 @@
// Used by UiAutomation
IBinder getWindowToken(int windowId, int userId);
- void notifyAccessibilityButtonClicked(int displayId);
+ void notifyAccessibilityButtonClicked(int displayId, String targetName);
void notifyAccessibilityButtonVisibilityChanged(boolean available);
// Requires Manifest.permission.MANAGE_ACCESSIBILITY
- void performAccessibilityShortcut();
+ void performAccessibilityShortcut(String targetName);
// Requires Manifest.permission.MANAGE_ACCESSIBILITY
List<String> getAccessibilityShortcutTargets(int shortcutType);
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index 5731e50..8565493 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -422,7 +422,7 @@
/**
* Update the displayed time if necessary and invalidate the view.
*/
- public void refresh() {
+ public void refreshTime() {
onTimeChanged();
invalidate();
}
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 9bdb4c1..d119b2e 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -16,6 +16,8 @@
package android.widget;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -42,8 +44,12 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
+import com.android.internal.annotations.GuardedBy;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
/**
* A toast is a view containing a quick little message for the user. The toast class
@@ -262,6 +268,29 @@
}
/**
+ * Adds a callback to be notified when the toast is shown or hidden.
+ *
+ * Note that if the toast is blocked for some reason you won't get a call back.
+ *
+ * @see #removeCallback(Callback)
+ */
+ public void addCallback(@NonNull Callback callback) {
+ checkNotNull(callback);
+ synchronized (mTN.mCallbacks) {
+ mTN.mCallbacks.add(callback);
+ }
+ }
+
+ /**
+ * Removes a callback previously added with {@link #addCallback(Callback)}.
+ */
+ public void removeCallback(@NonNull Callback callback) {
+ synchronized (mTN.mCallbacks) {
+ mTN.mCallbacks.remove(callback);
+ }
+ }
+
+ /**
* Gets the LayoutParams for the Toast window.
* @hide
*/
@@ -389,6 +418,9 @@
String mPackageName;
+ @GuardedBy("mCallbacks")
+ private final List<Callback> mCallbacks = new ArrayList<>();
+
static final long SHORT_DURATION_TIMEOUT = 4000;
static final long LONG_DURATION_TIMEOUT = 7000;
@@ -449,6 +481,12 @@
};
}
+ private List<Callback> getCallbacks() {
+ synchronized (mCallbacks) {
+ return new ArrayList<>(mCallbacks);
+ }
+ }
+
/**
* schedule handleShow into the right thread
*/
@@ -522,6 +560,9 @@
try {
mWM.addView(mView, mParams);
trySendAccessibilityEvent();
+ for (Callback callback : getCallbacks()) {
+ callback.onToastShown();
+ }
} catch (WindowManager.BadTokenException e) {
/* ignore */
}
@@ -564,8 +605,30 @@
} catch (RemoteException e) {
}
+ for (Callback callback : getCallbacks()) {
+ callback.onToastHidden();
+ }
mView = null;
}
}
}
+
+ /**
+ * Callback object to be called when the toast is shown or hidden.
+ *
+ * Callback methods will be called on the looper thread provided on construction.
+ *
+ * @see #addCallback(Callback)
+ */
+ public abstract static class Callback {
+ /**
+ * Called when the toast is displayed on the screen.
+ */
+ public void onToastShown() {}
+
+ /**
+ * Called when the toast is hidden.
+ */
+ public void onToastHidden() {}
+ }
}
diff --git a/core/java/com/android/internal/app/BlockedAppActivity.java b/core/java/com/android/internal/app/BlockedAppActivity.java
new file mode 100644
index 0000000..fbdbbfb
--- /dev/null
+++ b/core/java/com/android/internal/app/BlockedAppActivity.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2020 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.app;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.R;
+
+/**
+ * A dialog shown to the user when they try to launch an app that is not allowed in lock task
+ * mode. The intent to start this activity must be created with the static factory method provided
+ * below.
+ */
+public class BlockedAppActivity extends AlertActivity {
+
+ private static final String TAG = "BlockedAppActivity";
+ private static final String PACKAGE_NAME = "com.android.internal.app";
+ private static final String EXTRA_BLOCKED_PACKAGE = PACKAGE_NAME + ".extra.BLOCKED_PACKAGE";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Intent intent = getIntent();
+ int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, /* defaultValue= */ -1);
+ if (userId < 0) {
+ Slog.wtf(TAG, "Invalid user: " + userId);
+ finish();
+ return;
+ }
+
+ String packageName = intent.getStringExtra(EXTRA_BLOCKED_PACKAGE);
+ if (TextUtils.isEmpty(packageName)) {
+ Slog.wtf(TAG, "Invalid package: " + packageName);
+ finish();
+ return;
+ }
+
+ CharSequence appLabel = getAppLabel(userId, packageName);
+
+ mAlertParams.mTitle = getString(R.string.app_blocked_title);
+ mAlertParams.mMessage = getString(R.string.app_blocked_message, appLabel);
+ mAlertParams.mPositiveButtonText = getString(android.R.string.ok);
+ setupAlert();
+ }
+
+ private CharSequence getAppLabel(int userId, String packageName) {
+ PackageManager pm = getPackageManager();
+ try {
+ ApplicationInfo aInfo =
+ pm.getApplicationInfoAsUser(packageName, /* flags= */ 0, userId);
+ return aInfo.loadLabel(pm);
+ } catch (PackageManager.NameNotFoundException ne) {
+ Slog.e(TAG, "Package " + packageName + " not found", ne);
+ }
+ return packageName;
+ }
+
+
+ /** Creates an intent that launches {@link BlockedAppActivity}. */
+ public static Intent createIntent(int userId, String packageName) {
+ return new Intent()
+ .setClassName("android", BlockedAppActivity.class.getName())
+ .putExtra(Intent.EXTRA_USER_ID, userId)
+ .putExtra(EXTRA_BLOCKED_PACKAGE, packageName);
+ }
+}
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 673772a..39ea45a 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -1431,7 +1431,7 @@
// Isolated process / webview / app zygote should be gated by SELinux and file permission
// so they can't even traverse CE / DE directories.
if (pkg_data_info_list != nullptr
- && GetBoolProperty(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, false)) {
+ && GetBoolProperty(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true)) {
isolateAppData(env, pkg_data_info_list, uid, process_name, managed_nice_name,
fail_fn);
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7ecd9b7..2665e8a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4976,6 +4976,12 @@
android:process=":ui">
</activity>
+ <activity android:name="com.android.internal.app.BlockedAppActivity"
+ android:theme="@style/Theme.Dialog.Confirmation"
+ android:excludeFromRecents="true"
+ android:process=":ui">
+ </activity>
+
<activity android:name="com.android.settings.notification.NotificationAccessConfirmationActivity"
android:theme="@style/Theme.Dialog.Confirmation"
android:excludeFromRecents="true">
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9c08728..4475415 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3752,6 +3752,8 @@
</p>
-->
<attr name="canRequestFingerprintGestures" format="boolean" />
+ <!-- Attribute whether the accessibility service wants to be able to take screenshot. -->
+ <attr name="canTakeScreenshot" format="boolean" />
<!-- Animated image of the accessibility service purpose or behavior, to help users
understand how the service can help them.-->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 6cf6a68..9860830 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3007,6 +3007,7 @@
<public name="featureId" />
<public name="supportsInlineSuggestions" />
<public name="crossProfile" />
+ <public name="canTakeScreenshot"/>
</public-group>
<public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a0e4064..f977ea8 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -794,6 +794,11 @@
<string name="capability_desc_canCaptureFingerprintGestures">Can capture gestures performed on
the device\'s fingerprint sensor.</string>
+ <!-- Title for the capability of an accessibility service to take screenshot. [CHAR LIMIT=32] -->
+ <string name="capability_title_canTakeScreenshot">Take screenshot</string>
+ <!-- Description for the capability of an accessibility service to take screenshot. [CHAR LIMIT=NONE] -->
+ <string name="capability_desc_canTakeScreenshot">Can take a screenshot of the display.</string>
+
<!-- Permissions -->
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -4922,6 +4927,13 @@
<!-- Title for button to turn on work profile. [CHAR LIMIT=NONE] -->
<string name="work_mode_turn_on">Turn on</string>
+ <!-- Title of the dialog that is shown when the user tries to launch a suspended application [CHAR LIMIT=50] -->
+ <string name="app_blocked_title">App is not available</string>
+ <!-- Default message shown in the dialog that is shown when the user tries to launch a suspended application [CHAR LIMIT=NONE] -->
+ <string name="app_blocked_message">
+ <xliff:g id="app_name" example="Gmail">%1$s</xliff:g> is not available right now.
+ </string>
+
<!-- Message displayed in dialog when app is too old to run on this verison of android. [CHAR LIMIT=NONE] -->
<string name="deprecated_target_sdk_message">This app was built for an older version of Android and may not work properly. Try checking for updates, or contact the developer.</string>
<!-- Title for button to see application detail in app store which it came from - it may allow user to update to newer version. [CHAR LIMIT=50] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index fd46916..30dbfc7 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3036,6 +3036,9 @@
<java-symbol type="string" name="app_suspended_more_details" />
<java-symbol type="string" name="app_suspended_default_message" />
+ <java-symbol type="string" name="app_blocked_title" />
+ <java-symbol type="string" name="app_blocked_message" />
+
<!-- Used internally for assistant to launch activity transitions -->
<java-symbol type="id" name="cross_task_transition" />
@@ -3808,4 +3811,9 @@
<java-symbol type="dimen" name="waterfall_display_top_edge_size" />
<java-symbol type="dimen" name="waterfall_display_right_edge_size" />
<java-symbol type="dimen" name="waterfall_display_bottom_edge_size" />
+
+ <!-- Accessibility take screenshot -->
+ <java-symbol type="string" name="capability_desc_canTakeScreenshot" />
+ <java-symbol type="string" name="capability_title_canTakeScreenshot" />
+
</resources>
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index 836cb95..f151b81 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -19,9 +19,11 @@
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.content.pm.ParceledListSlice;
+import android.graphics.Bitmap;
import android.graphics.Region;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.RemoteCallback;
/**
* Stub implementation of IAccessibilityServiceConnection so each test doesn't need to implement
@@ -147,4 +149,10 @@
public int getWindowIdForLeashToken(IBinder token) {
return -1;
}
+
+ public Bitmap takeScreenshot(int displayId) {
+ return null;
+ }
+
+ public void takeScreenshotWithCallback(int displayId, RemoteCallback callback) {}
}
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index 82854e5..6784ede 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -348,7 +348,7 @@
verify(mAlertDialog).show();
verify(mAccessibilityManagerService, atLeastOnce()).getInstalledAccessibilityServiceList(
anyInt());
- verify(mAccessibilityManagerService, times(0)).performAccessibilityShortcut();
+ verify(mAccessibilityManagerService, times(0)).performAccessibilityShortcut(null);
verify(mFrameworkObjectProvider, times(0)).getTextToSpeech(any(), any());
}
@@ -365,7 +365,7 @@
assertEquals(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS,
mLayoutParams.privateFlags
& WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS);
- verify(mAccessibilityManagerService, times(1)).performAccessibilityShortcut();
+ verify(mAccessibilityManagerService, times(1)).performAccessibilityShortcut(null);
}
@Test
@@ -433,7 +433,7 @@
verifyZeroInteractions(mAlertDialogBuilder, mAlertDialog);
verify(mToast).show();
- verify(mAccessibilityManagerService).performAccessibilityShortcut();
+ verify(mAccessibilityManagerService).performAccessibilityShortcut(null);
}
@Test
@@ -459,7 +459,7 @@
when(mServiceInfo.loadSummary(any())).thenReturn(null);
Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 1);
getController().performAccessibilityShortcut();
- verify(mAccessibilityManagerService).performAccessibilityShortcut();
+ verify(mAccessibilityManagerService).performAccessibilityShortcut(null);
}
@Test
@@ -471,7 +471,7 @@
getController().performAccessibilityShortcut();
verifyZeroInteractions(mToast);
- verify(mAccessibilityManagerService).performAccessibilityShortcut();
+ verify(mAccessibilityManagerService).performAccessibilityShortcut(null);
}
@Test
@@ -485,7 +485,7 @@
getController().performAccessibilityShortcut();
verifyZeroInteractions(mToast);
- verify(mAccessibilityManagerService).performAccessibilityShortcut();
+ verify(mAccessibilityManagerService).performAccessibilityShortcut(null);
}
@Test
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
index 17aacb9..fedde42 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
@@ -308,6 +308,9 @@
if (spec.isStrongBoxBacked()) {
flags |= KeyStore.FLAG_STRONGBOX;
}
+ if (spec.isCriticalToDeviceEncryption()) {
+ flags |= KeyStore.FLAG_CRITICAL_TO_DEVICE_ENCRYPTION;
+ }
String keyAliasInKeystore = Credentials.USER_PRIVATE_KEY + spec.getKeystoreAlias();
KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
boolean success = false;
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index 91aac83..c52fd48 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -18,10 +18,8 @@
import android.annotation.Nullable;
import android.security.Credentials;
-import android.security.GateKeeper;
import android.security.KeyPairGeneratorSpec;
import android.security.KeyStore;
-import android.security.KeyStoreException;
import android.security.keymaster.KeyCharacteristics;
import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterCertificateChain;
@@ -458,6 +456,9 @@
if (mSpec.isStrongBoxBacked()) {
flags |= KeyStore.FLAG_STRONGBOX;
}
+ if (mSpec.isCriticalToDeviceEncryption()) {
+ flags |= KeyStore.FLAG_CRITICAL_TO_DEVICE_ENCRYPTION;
+ }
byte[] additionalEntropy =
KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 52ff9e0..450dd33 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -271,6 +271,7 @@
private final boolean mIsStrongBoxBacked;
private final boolean mUserConfirmationRequired;
private final boolean mUnlockedDeviceRequired;
+ private final boolean mCriticalToDeviceEncryption;
/*
* ***NOTE***: All new fields MUST also be added to the following:
* ParcelableKeyGenParameterSpec class.
@@ -307,7 +308,8 @@
boolean invalidatedByBiometricEnrollment,
boolean isStrongBoxBacked,
boolean userConfirmationRequired,
- boolean unlockedDeviceRequired) {
+ boolean unlockedDeviceRequired,
+ boolean criticalToDeviceEncryption) {
if (TextUtils.isEmpty(keyStoreAlias)) {
throw new IllegalArgumentException("keyStoreAlias must not be empty");
}
@@ -357,6 +359,7 @@
mIsStrongBoxBacked = isStrongBoxBacked;
mUserConfirmationRequired = userConfirmationRequired;
mUnlockedDeviceRequired = unlockedDeviceRequired;
+ mCriticalToDeviceEncryption = criticalToDeviceEncryption;
}
/**
@@ -710,6 +713,16 @@
}
/**
+ * Return whether this key is critical to the device encryption flow.
+ *
+ * @see android.security.KeyStore#FLAG_CRITICAL_TO_DEVICE_ENCRYPTION
+ * @hide
+ */
+ public boolean isCriticalToDeviceEncryption() {
+ return mCriticalToDeviceEncryption;
+ }
+
+ /**
* Builder of {@link KeyGenParameterSpec} instances.
*/
public final static class Builder {
@@ -741,6 +754,7 @@
private boolean mIsStrongBoxBacked = false;
private boolean mUserConfirmationRequired;
private boolean mUnlockedDeviceRequired = false;
+ private boolean mCriticalToDeviceEncryption = false;
/**
* Creates a new instance of the {@code Builder}.
@@ -804,6 +818,7 @@
mIsStrongBoxBacked = sourceSpec.isStrongBoxBacked();
mUserConfirmationRequired = sourceSpec.isUserConfirmationRequired();
mUnlockedDeviceRequired = sourceSpec.isUnlockedDeviceRequired();
+ mCriticalToDeviceEncryption = sourceSpec.isCriticalToDeviceEncryption();
}
/**
@@ -1339,6 +1354,20 @@
}
/**
+ * Set whether this key is critical to the device encryption flow
+ *
+ * This is a special flag only available to system servers to indicate the current key
+ * is part of the device encryption flow.
+ *
+ * @see android.security.KeyStore#FLAG_CRITICAL_TO_DEVICE_ENCRYPTION
+ * @hide
+ */
+ public Builder setCriticalToDeviceEncryption(boolean critical) {
+ mCriticalToDeviceEncryption = critical;
+ return this;
+ }
+
+ /**
* Builds an instance of {@code KeyGenParameterSpec}.
*/
@NonNull
@@ -1370,7 +1399,8 @@
mInvalidatedByBiometricEnrollment,
mIsStrongBoxBacked,
mUserConfirmationRequired,
- mUnlockedDeviceRequired);
+ mUnlockedDeviceRequired,
+ mCriticalToDeviceEncryption);
}
}
}
diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
index d8030fb..98e4589 100644
--- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
@@ -16,8 +16,8 @@
package android.security.keystore;
-import android.os.Parcelable;
import android.os.Parcel;
+import android.os.Parcelable;
import java.math.BigInteger;
import java.security.spec.AlgorithmParameterSpec;
@@ -105,6 +105,7 @@
out.writeBoolean(mSpec.isStrongBoxBacked());
out.writeBoolean(mSpec.isUserConfirmationRequired());
out.writeBoolean(mSpec.isUnlockedDeviceRequired());
+ out.writeBoolean(mSpec.isCriticalToDeviceEncryption());
}
private static Date readDateOrNull(Parcel in) {
@@ -160,6 +161,7 @@
final boolean isStrongBoxBacked = in.readBoolean();
final boolean userConfirmationRequired = in.readBoolean();
final boolean unlockedDeviceRequired = in.readBoolean();
+ final boolean criticalToDeviceEncryption = in.readBoolean();
// The KeyGenParameterSpec is intentionally not constructed using a Builder here:
// The intention is for this class to break if new parameters are added to the
// KeyGenParameterSpec constructor (whereas using a builder would silently drop them).
@@ -190,7 +192,8 @@
invalidatedByBiometricEnrollment,
isStrongBoxBacked,
userConfirmationRequired,
- unlockedDeviceRequired);
+ unlockedDeviceRequired,
+ criticalToDeviceEncryption);
}
public static final @android.annotation.NonNull Creator<ParcelableKeyGenParameterSpec> CREATOR = new Creator<ParcelableKeyGenParameterSpec>() {
diff --git a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
index fca2775..b7d72fc 100644
--- a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
+++ b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
@@ -84,6 +84,7 @@
.setIsStrongBoxBacked(true)
.setUserConfirmationRequired(true)
.setUnlockedDeviceRequired(true)
+ .setCriticalToDeviceEncryption(true)
.build();
}
@@ -115,6 +116,7 @@
assertThat(spec.isStrongBoxBacked(), is(true));
assertThat(spec.isUserConfirmationRequired(), is(true));
assertThat(spec.isUnlockedDeviceRequired(), is(true));
+ assertThat(spec.isCriticalToDeviceEncryption(), is(true));
}
private Parcel parcelForReading(ParcelableKeyGenParameterSpec spec) {
diff --git a/media/java/android/media/tv/tuner/Lnb.java b/media/java/android/media/tv/tuner/Lnb.java
index c7cc9e6d..8e579bf 100644
--- a/media/java/android/media/tv/tuner/Lnb.java
+++ b/media/java/android/media/tv/tuner/Lnb.java
@@ -23,7 +23,6 @@
import android.annotation.SystemApi;
import android.content.Context;
import android.hardware.tv.tuner.V1_0.Constants;
-import android.media.tv.tuner.Tuner.LnbCallback;
import android.media.tv.tuner.TunerConstants.Result;
import java.lang.annotation.Retention;
diff --git a/media/java/android/media/tv/tuner/LnbCallback.java b/media/java/android/media/tv/tuner/LnbCallback.java
new file mode 100644
index 0000000..99bbf86
--- /dev/null
+++ b/media/java/android/media/tv/tuner/LnbCallback.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+
+/**
+ * Callback interface for receiving information from LNBs.
+ *
+ * @hide
+ */
+public interface LnbCallback {
+ /**
+ * Invoked when there is a LNB event.
+ */
+ void onEvent(int lnbEventType);
+
+ /**
+ * Invoked when there is a new DiSEqC message.
+ *
+ * @param diseqcMessage a byte array of data for DiSEqC (Digital Satellite
+ * Equipment Control) message which is specified by EUTELSAT Bus Functional
+ * Specification Version 4.2.
+ */
+ void onDiseqcMessage(byte[] diseqcMessage);
+}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index e3dfaeb..6f5ba84 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -137,29 +137,10 @@
private static native DemuxCapabilities nativeGetDemuxCapabilities();
- /**
- * LNB Callback.
- *
- * @hide
- */
- public interface LnbCallback {
- /**
- * Invoked when there is a LNB event.
- */
- void onEvent(int lnbEventType);
-
- /**
- * Invoked when there is a new DiSEqC message.
- *
- * @param diseqcMessage a byte array of data for DiSEqC (Digital Satellite
- * Equipment Control) message which is specified by EUTELSAT Bus Functional
- * Specification Version 4.2.
- */
- void onDiseqcMessage(byte[] diseqcMessage);
- }
/**
* Callback interface for receiving information from the corresponding filters.
+ * TODO: remove
*/
public interface FilterCallback {
/**
@@ -178,22 +159,6 @@
void onFilterStatusChanged(@NonNull Filter filter, @FilterStatus int status);
}
- /**
- * DVR Callback.
- *
- * @hide
- */
- public interface DvrCallback {
- /**
- * Invoked when record status changed.
- */
- void onRecordStatus(int status);
- /**
- * Invoked when playback status changed.
- */
- void onPlaybackStatus(int status);
- }
-
@Nullable
private EventHandler createEventHandler() {
Looper looper;
diff --git a/media/java/android/media/tv/tuner/dvr/Dvr.java b/media/java/android/media/tv/tuner/dvr/Dvr.java
index f90042b..95508d3 100644
--- a/media/java/android/media/tv/tuner/dvr/Dvr.java
+++ b/media/java/android/media/tv/tuner/dvr/Dvr.java
@@ -18,7 +18,6 @@
import android.annotation.BytesLong;
import android.annotation.NonNull;
-import android.media.tv.tuner.Tuner.DvrCallback;
import android.media.tv.tuner.Tuner.Filter;
import android.media.tv.tuner.TunerConstants.Result;
import android.os.ParcelFileDescriptor;
diff --git a/media/java/android/media/tv/tuner/dvr/DvrCallback.java b/media/java/android/media/tv/tuner/dvr/DvrCallback.java
new file mode 100644
index 0000000..3d140f0
--- /dev/null
+++ b/media/java/android/media/tv/tuner/dvr/DvrCallback.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.dvr;
+
+/**
+ * Callback interface for receiving information from DVR interfaces.
+ *
+ * @hide
+ */
+public interface DvrCallback {
+ /**
+ * Invoked when record status changed.
+ */
+ void onRecordStatusChanged(int status);
+ /**
+ * Invoked when playback status changed.
+ */
+ void onPlaybackStatusChanged(int status);
+}
diff --git a/media/java/android/media/tv/tuner/filter/FilterCallback.java b/media/java/android/media/tv/tuner/filter/FilterCallback.java
new file mode 100644
index 0000000..888adc5
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/FilterCallback.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.filter;
+
+import android.annotation.NonNull;
+import android.media.tv.tuner.TunerConstants.FilterStatus;
+
+/**
+ * Callback interface for receiving information from the corresponding filters.
+ *
+ * @hide
+ */
+public interface FilterCallback {
+ /**
+ * Invoked when there are filter events.
+ *
+ * @param filter the corresponding filter which sent the events.
+ * @param events the filter events sent from the filter.
+ */
+ void onFilterEvent(@NonNull Filter filter, @NonNull FilterEvent[] events);
+ /**
+ * Invoked when filter status changed.
+ *
+ * @param filter the corresponding filter whose status is changed.
+ * @param status the new status of the filter.
+ */
+ void onFilterStatusChanged(@NonNull Filter filter, @FilterStatus int status);
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 747ceb1..50d3a5d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -262,13 +262,28 @@
}
}
+ /**
+ * Connect this device.
+ *
+ * @param connectAllProfiles {@code true} to connect all profile, {@code false} otherwise.
+ *
+ * @deprecated use {@link #connect()} instead.
+ */
+ @Deprecated
public void connect(boolean connectAllProfiles) {
+ connect();
+ }
+
+ /**
+ * Connect this device.
+ */
+ public void connect() {
if (!ensurePaired()) {
return;
}
mConnectAttempted = SystemClock.elapsedRealtime();
- connectWithoutResettingTimer(connectAllProfiles);
+ connectAllEnabledProfiles();
}
public long getHiSyncId() {
@@ -289,10 +304,10 @@
void onBondingDockConnect() {
// Attempt to connect if UUIDs are available. Otherwise,
// we will connect when the ACTION_UUID intent arrives.
- connect(false);
+ connect();
}
- private void connectWithoutResettingTimer(boolean connectAllProfiles) {
+ private void connectAllEnabledProfiles() {
synchronized (mProfileLock) {
// Try to initialize the profiles if they were not.
if (mProfiles.isEmpty()) {
@@ -307,36 +322,7 @@
return;
}
- int preferredProfiles = 0;
- for (LocalBluetoothProfile profile : mProfiles) {
- if (connectAllProfiles ? profile.accessProfileEnabled()
- : profile.isAutoConnectable()) {
- if (profile.isPreferred(mDevice)) {
- ++preferredProfiles;
- connectInt(profile);
- }
- }
- }
- if (BluetoothUtils.D) Log.d(TAG, "Preferred profiles = " + preferredProfiles);
-
- if (preferredProfiles == 0) {
- connectAutoConnectableProfiles();
- }
- }
- }
-
- private void connectAutoConnectableProfiles() {
- if (!ensurePaired()) {
- return;
- }
-
- synchronized (mProfileLock) {
- for (LocalBluetoothProfile profile : mProfiles) {
- if (profile.isAutoConnectable()) {
- profile.setPreferred(mDevice, true);
- connectInt(profile);
- }
- }
+ mLocalAdapter.connectAllEnabledProfiles(mDevice);
}
}
@@ -703,7 +689,7 @@
*/
if (!mProfiles.isEmpty()
&& ((mConnectAttempted + timeout) > SystemClock.elapsedRealtime())) {
- connectWithoutResettingTimer(false);
+ connectAllEnabledProfiles();
}
dispatchAttributesChanged();
@@ -722,7 +708,7 @@
refresh();
if (bondState == BluetoothDevice.BOND_BONDED && mDevice.isBondingInitiatedLocally()) {
- connect(false);
+ connect();
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 70b56ed..e85a102 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -122,7 +122,7 @@
final CachedBluetoothDevice cachedDevice =
((BluetoothMediaDevice) device).getCachedDevice();
if (!cachedDevice.isConnected() && !cachedDevice.isBusy()) {
- cachedDevice.connect(true);
+ cachedDevice.connect();
return;
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java
index 853c77e..05a6ce4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java
@@ -40,7 +40,7 @@
final NetworkTemplate mobileAll = NetworkTemplate.buildTemplateMobileAll(
telephonyManager.createForSubscriptionId(subId).getSubscriberId());
- if (!subscriptionManager.isActiveSubId(subId)) {
+ if (!subscriptionManager.isActiveSubscriptionId(subId)) {
Log.i(TAG, "Subscription is not active: " + subId);
return mobileAll;
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/OWNER b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/OWNER
new file mode 100644
index 0000000..5c2a7b8
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/OWNER
@@ -0,0 +1,4 @@
+# People who can approve changes for submission
+arcwang@google.com
+govenliu@google.com
+qal@google.com
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index 894aa78..c780a64 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -127,7 +127,7 @@
mLocalMediaManager.registerCallback(mCallback);
mLocalMediaManager.connectDevice(device);
- verify(cachedDevice).connect(true);
+ verify(cachedDevice).connect();
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java
index 7be9fee..b0a647e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java
@@ -72,13 +72,13 @@
when(mTelephonyManager.getSubscriberId(SUB_ID)).thenReturn(SUBSCRIBER_ID);
when(mTelephonyManager.getSubscriberId(SUB_ID_2)).thenReturn(SUBSCRIBER_ID_2);
when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
- when(mSubscriptionManager.isActiveSubId(anyInt())).thenReturn(true);
+ when(mSubscriptionManager.isActiveSubscriptionId(anyInt())).thenReturn(true);
}
@Test
@Ignore
public void getMobileTemplate_infoNull_returnMobileAll() {
- when(mSubscriptionManager.isActiveSubId(SUB_ID)).thenReturn(false);
+ when(mSubscriptionManager.isActiveSubscriptionId(SUB_ID)).thenReturn(false);
final NetworkTemplate networkTemplate = DataUsageUtils.getMobileTemplate(mContext, SUB_ID);
assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID)).isTrue();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
index c288d51..2bd20a9 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
@@ -26,7 +26,6 @@
import android.graphics.drawable.ColorDrawable;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -59,7 +58,6 @@
}
@Test
- @Ignore
public void refresh_openNetwork_updateContentDescription() {
final String ssid = "ssid";
final String summary = "connected";
@@ -74,7 +72,7 @@
assertThat(AccessPointPreference.buildContentDescription(
RuntimeEnvironment.application, pref, ap))
- .isEqualTo("ssid,connected,Wifi signal full.,Secure network");
+ .isEqualTo("ssid,connected,Wifi disconnected.,Secure network");
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/OWNER b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/OWNER
new file mode 100644
index 0000000..5c2a7b8
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/OWNER
@@ -0,0 +1,4 @@
+# People who can approve changes for submission
+arcwang@google.com
+govenliu@google.com
+qal@google.com
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 1e0c1d8..4443524 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -20,6 +20,7 @@
import static android.os.Process.ROOT_UID;
import static android.os.Process.SHELL_UID;
import static android.os.Process.SYSTEM_UID;
+import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY;
import android.Manifest;
@@ -3304,7 +3305,7 @@
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 185;
+ private static final int SETTINGS_VERSION = 186;
private final int mUserId;
@@ -4547,6 +4548,32 @@
currentVersion = 185;
}
+ if (currentVersion == 185) {
+ // Deprecate ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, and migrate it
+ // to ACCESSIBILITY_BUTTON_TARGET_COMPONENT.
+ final SettingsState secureSettings = getSecureSettingsLocked(userId);
+ final Setting magnifyNavbarEnabled = secureSettings.getSettingLocked(
+ Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
+ if ("1".equals(magnifyNavbarEnabled.getValue())) {
+ secureSettings.insertSettingLocked(
+ Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
+ ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER,
+ null /* tag */, false /* makeDefault */,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ } else {
+ // Clear a11y button targets list setting. A11yManagerService will end up
+ // adding all legacy enabled services that want the button to the list, so
+ // there's no need to keep tracking them.
+ secureSettings.insertSettingLocked(
+ Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
+ null, null /* tag */, false /* makeDefault */,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+ secureSettings.deleteSettingLocked(
+ Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
+ currentVersion = 186;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 7278225..1a1a480 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -734,7 +734,8 @@
Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE,
Settings.Secure.FACE_UNLOCK_RE_ENROLL,
Settings.Secure.TAP_GESTURE,
- Settings.Secure.WINDOW_MAGNIFICATION);
+ Settings.Secure.WINDOW_MAGNIFICATION,
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER);
@Test
public void systemSettingsBackedUpOrBlacklisted() {
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 2a1e74e..df77b5b 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -211,6 +211,7 @@
<!-- accessibility -->
<uses-permission android:name="android.permission.MODIFY_ACCESSIBILITY_DATA" />
+ <uses-permission android:name="android.permission.MANAGE_ACCESSIBILITY" />
<!-- to control accessibility volume -->
<uses-permission android:name="android.permission.CHANGE_ACCESSIBILITY_VOLUME" />
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index df0dc46..e475ef1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -387,8 +387,8 @@
* Refresh the time of the clock, due to either time tick broadcast or doze time tick alarm.
*/
public void refresh() {
- mClockView.refresh();
- mClockViewBold.refresh();
+ mClockView.refreshTime();
+ mClockViewBold.refreshTime();
if (mClockPlugin != null) {
mClockPlugin.onTimeTick();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java
index eba2400..99e122e 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java
@@ -188,7 +188,7 @@
public void onTimeTick() {
mAnalogClock.onTimeChanged();
mBigClockView.onTimeChanged();
- mLockClock.refresh();
+ mLockClock.refreshTime();
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java
index 3a2fbe5..fac923c01 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java
@@ -195,7 +195,7 @@
public void onTimeTick() {
mAnalogClock.onTimeChanged();
mView.onTimeChanged();
- mLockClock.refresh();
+ mLockClock.refreshTime();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
index 3bbd722..820c042 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
@@ -176,12 +176,14 @@
}
entry.ranking = newRanking
- val oldSbn = entry.sbn.cloneLight()
val newOverrideGroupKey = newRanking.overrideGroupKey
- if (!Objects.equals(oldSbn.overrideGroupKey, newOverrideGroupKey)) {
+ if (!Objects.equals(entry.sbn.overrideGroupKey, newOverrideGroupKey)) {
+ val oldGroupKey = entry.sbn.groupKey
+ val oldIsGroup = entry.sbn.isGroup
+ val oldIsGroupSummary = entry.sbn.notification.isGroupSummary
entry.sbn.overrideGroupKey = newOverrideGroupKey
- // TODO: notify group manager here?
- groupManager.onEntryUpdated(entry, oldSbn)
+ groupManager.onEntryUpdated(entry, oldGroupKey, oldIsGroup,
+ oldIsGroupSummary)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index a6842ba..4f56f56 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -157,7 +157,6 @@
private int mNavigationIconHints = 0;
private @TransitionMode int mNavigationBarMode;
private AccessibilityManager mAccessibilityManager;
- private MagnificationContentObserver mMagnificationObserver;
private ContentResolver mContentResolver;
private boolean mAssistantAvailable;
@@ -303,11 +302,6 @@
mWindowManager = getContext().getSystemService(WindowManager.class);
mAccessibilityManager = getContext().getSystemService(AccessibilityManager.class);
mContentResolver = getContext().getContentResolver();
- mMagnificationObserver = new MagnificationContentObserver(
- getContext().getMainThreadHandler());
- mContentResolver.registerContentObserver(Settings.Secure.getUriFor(
- Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED), false,
- mMagnificationObserver, UserHandle.USER_ALL);
mContentResolver.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.ASSISTANT),
false /* notifyForDescendants */, mAssistContentObserver, UserHandle.USER_ALL);
@@ -329,7 +323,6 @@
super.onDestroy();
mNavigationModeController.removeListener(this);
mAccessibilityManagerWrapper.removeCallback(mAccessibilityListener);
- mContentResolver.unregisterContentObserver(mMagnificationObserver);
mContentResolver.unregisterContentObserver(mAssistContentObserver);
}
@@ -969,28 +962,18 @@
* @param outFeedbackEnabled if non-null, sets it to true if accessibility feedback is enabled.
*/
public int getA11yButtonState(@Nullable boolean[] outFeedbackEnabled) {
- int requestingServices = 0;
- try {
- if (Settings.Secure.getIntForUser(mContentResolver,
- Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
- UserHandle.USER_CURRENT) == 1) {
- requestingServices++;
- }
- } catch (Settings.SettingNotFoundException e) {
- }
-
boolean feedbackEnabled = false;
// AccessibilityManagerService resolves services for the current user since the local
// AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission
final List<AccessibilityServiceInfo> services =
mAccessibilityManager.getEnabledAccessibilityServiceList(
AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
+ final List<String> a11yButtonTargets =
+ mAccessibilityManager.getAccessibilityShortcutTargets(
+ AccessibilityManager.ACCESSIBILITY_BUTTON);
+ final int requestingServices = a11yButtonTargets.size();
for (int i = services.size() - 1; i >= 0; --i) {
AccessibilityServiceInfo info = services.get(i);
- if ((info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0) {
- requestingServices++;
- }
-
if (info.feedbackType != 0 && info.feedbackType !=
AccessibilityServiceInfo.FEEDBACK_GENERIC) {
feedbackEnabled = true;
@@ -1114,18 +1097,6 @@
private final AccessibilityServicesStateChangeListener mAccessibilityListener =
this::updateAccessibilityServicesState;
- private class MagnificationContentObserver extends ContentObserver {
-
- public MagnificationContentObserver(Handler handler) {
- super(handler);
- }
-
- @Override
- public void onChange(boolean selfChange) {
- NavigationBarFragment.this.updateAccessibilityServicesState(mAccessibilityManager);
- }
- }
-
private final Consumer<Integer> mRotationWatcher = rotation -> {
if (mNavigationBarView != null
&& mNavigationBarView.needsReorient(rotation)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index e11fc1b..8c947ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -116,7 +116,13 @@
*/
private void onEntryRemovedInternal(NotificationEntry removed,
final StatusBarNotification sbn) {
- String groupKey = getGroupKey(sbn);
+ onEntryRemovedInternal(removed, sbn.getGroupKey(), sbn.isGroup(),
+ sbn.getNotification().isGroupSummary());
+ }
+
+ private void onEntryRemovedInternal(NotificationEntry removed, String notifGroupKey, boolean
+ isGroup, boolean isGroupSummary) {
+ String groupKey = getGroupKey(removed.getKey(), notifGroupKey);
final NotificationGroup group = mGroupMap.get(groupKey);
if (group == null) {
// When an app posts 2 different notifications as summary of the same group, then a
@@ -125,7 +131,7 @@
// the close future. See b/23676310 for reference.
return;
}
- if (isGroupChild(sbn)) {
+ if (isGroupChild(removed.getKey(), isGroup, isGroupSummary)) {
group.children.remove(removed.getKey());
} else {
group.summary = null;
@@ -229,7 +235,7 @@
private int getNumberOfIsolatedChildren(String groupKey) {
int count = 0;
for (StatusBarNotification sbn : mIsolatedEntries.values()) {
- if (sbn.getGroupKey().equals(groupKey) && isIsolated(sbn)) {
+ if (sbn.getGroupKey().equals(groupKey) && isIsolated(sbn.getKey())) {
count++;
}
}
@@ -238,31 +244,47 @@
private NotificationEntry getIsolatedChild(String groupKey) {
for (StatusBarNotification sbn : mIsolatedEntries.values()) {
- if (sbn.getGroupKey().equals(groupKey) && isIsolated(sbn)) {
+ if (sbn.getGroupKey().equals(groupKey) && isIsolated(sbn.getKey())) {
return mGroupMap.get(sbn.getKey()).summary;
}
}
return null;
}
- public void onEntryUpdated(NotificationEntry entry,
- StatusBarNotification oldNotification) {
- String oldKey = oldNotification.getGroupKey();
- String newKey = entry.getSbn().getGroupKey();
- boolean groupKeysChanged = !oldKey.equals(newKey);
- boolean wasGroupChild = isGroupChild(oldNotification);
+ /**
+ * Update an entry's group information
+ * @param entry notification entry to update
+ * @param oldNotification previous notification info before this update
+ */
+ public void onEntryUpdated(NotificationEntry entry, StatusBarNotification oldNotification) {
+ onEntryUpdated(entry, oldNotification.getGroupKey(), oldNotification.isGroup(),
+ oldNotification.getNotification().isGroupSummary());
+ }
+
+ /**
+ * Updates an entry's group information
+ * @param entry notification entry to update
+ * @param oldGroupKey the notification's previous group key before this update
+ * @param oldIsGroup whether this notification was a group before this update
+ * @param oldIsGroupSummary whether this notification was a group summary before this update
+ */
+ public void onEntryUpdated(NotificationEntry entry, String oldGroupKey, boolean oldIsGroup,
+ boolean oldIsGroupSummary) {
+ String newGroupKey = entry.getSbn().getGroupKey();
+ boolean groupKeysChanged = !oldGroupKey.equals(newGroupKey);
+ boolean wasGroupChild = isGroupChild(entry.getKey(), oldIsGroup, oldIsGroupSummary);
boolean isGroupChild = isGroupChild(entry.getSbn());
mIsUpdatingUnchangedGroup = !groupKeysChanged && wasGroupChild == isGroupChild;
- if (mGroupMap.get(getGroupKey(oldNotification)) != null) {
- onEntryRemovedInternal(entry, oldNotification);
+ if (mGroupMap.get(getGroupKey(entry.getKey(), oldGroupKey)) != null) {
+ onEntryRemovedInternal(entry, oldGroupKey, oldIsGroup, oldIsGroupSummary);
}
onEntryAdded(entry);
mIsUpdatingUnchangedGroup = false;
- if (isIsolated(entry.getSbn())) {
+ if (isIsolated(entry.getSbn().getKey())) {
mIsolatedEntries.put(entry.getKey(), entry.getSbn());
if (groupKeysChanged) {
- updateSuppression(mGroupMap.get(oldKey));
- updateSuppression(mGroupMap.get(newKey));
+ updateSuppression(mGroupMap.get(oldGroupKey));
+ updateSuppression(mGroupMap.get(newGroupKey));
}
} else if (!wasGroupChild && isGroupChild) {
onEntryBecomingChild(entry);
@@ -418,10 +440,14 @@
* @return the key of the notification
*/
public String getGroupKey(StatusBarNotification sbn) {
- if (isIsolated(sbn)) {
- return sbn.getKey();
+ return getGroupKey(sbn.getKey(), sbn.getGroupKey());
+ }
+
+ private String getGroupKey(String key, String groupKey) {
+ if (isIsolated(key)) {
+ return key;
}
- return sbn.getGroupKey();
+ return groupKey;
}
/** @return group expansion state after toggling. */
@@ -434,8 +460,8 @@
return group.expanded;
}
- private boolean isIsolated(StatusBarNotification sbn) {
- return mIsolatedEntries.containsKey(sbn.getKey());
+ private boolean isIsolated(String sbnKey) {
+ return mIsolatedEntries.containsKey(sbnKey);
}
/**
@@ -445,7 +471,7 @@
* @return true if it is visually a group summary
*/
public boolean isGroupSummary(StatusBarNotification sbn) {
- if (isIsolated(sbn)) {
+ if (isIsolated(sbn.getKey())) {
return true;
}
return sbn.getNotification().isGroupSummary();
@@ -458,10 +484,14 @@
* @return true if it is visually a group child
*/
public boolean isGroupChild(StatusBarNotification sbn) {
- if (isIsolated(sbn)) {
+ return isGroupChild(sbn.getKey(), sbn.isGroup(), sbn.getNotification().isGroupSummary());
+ }
+
+ private boolean isGroupChild(String key, boolean isGroup, boolean isGroupSummary) {
+ if (isIsolated(key)) {
return false;
}
- return sbn.isGroup() && !sbn.getNotification().isGroupSummary();
+ return isGroup && !isGroupSummary;
}
@Override
diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml
index ca290c6..19e8d69 100644
--- a/packages/Tethering/res/values/config.xml
+++ b/packages/Tethering/res/values/config.xml
@@ -38,8 +38,9 @@
<!-- List of regexpressions describing the interface (if any) that represent tetherable
Wifi P2P interfaces. If the device doesn't want to support tethering over Wifi P2p this
- should be empty. An example would be "p2p-p2p.*" -->
+ should be empty. An example would be "p2p-p2p\\d-.*" -->
<string-array translatable="false" name="config_tether_wifi_p2p_regexs">
+ <item>"p2p-p2p\\d-.*"</item>
</string-array>
<!-- List of regexpressions describing the interface (if any) that represent tetherable
@@ -100,7 +101,7 @@
<!-- If the mobile hotspot feature requires provisioning, a package name and class name
can be provided to launch a supported application that provisions the devices.
- EntitlementManager will send an inent to Settings with the specified package name and
+ EntitlementManager will send an intent to Settings with the specified package name and
class name in extras to launch provision app.
TODO: note what extras here.
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
index 4e2a2c1..1cabc8d 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
@@ -27,8 +27,6 @@
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_PROVISION_FAILED;
-import static com.android.internal.R.string.config_wifi_tether_enable;
-
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -36,7 +34,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.res.Resources;
import android.net.util.SharedLog;
import android.os.Bundle;
import android.os.Handler;
@@ -54,6 +51,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.StateMachine;
+import com.android.networkstack.tethering.R;
import java.io.PrintWriter;
@@ -75,9 +73,7 @@
"com.android.server.connectivity.tethering.PROVISIONING_RECHECK_ALARM";
private static final String EXTRA_SUBID = "subId";
- // {@link ComponentName} of the Service used to run tether provisioning.
- private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(
- Resources.getSystem().getString(config_wifi_tether_enable));
+ private final ComponentName mSilentProvisioningService;
private static final int MS_PER_HOUR = 60 * 60 * 1000;
private static final int EVENT_START_PROVISIONING = 0;
private static final int EVENT_STOP_PROVISIONING = 1;
@@ -122,6 +118,8 @@
mHandler = new EntitlementHandler(masterHandler.getLooper());
mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_PROVISIONING_ALARM),
null, mHandler);
+ mSilentProvisioningService = ComponentName.unflattenFromString(
+ mContext.getResources().getString(R.string.config_wifi_tether_enable));
}
public void setOnUiEntitlementFailedListener(final OnUiEntitlementFailedListener listener) {
@@ -377,7 +375,7 @@
intent.putExtra(EXTRA_RUN_PROVISION, true);
intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
intent.putExtra(EXTRA_SUBID, subId);
- intent.setComponent(TETHER_SERVICE);
+ intent.setComponent(mSilentProvisioningService);
// Only admin user can change tethering and SilentTetherProvisioning don't need to
// show UI, it is fine to always start setting's background service as system user.
mContext.startService(intent);
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
index b301d3d..37e0cc1 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
@@ -20,6 +20,7 @@
import static android.hardware.usb.UsbManager.USB_CONFIGURED;
import static android.hardware.usb.UsbManager.USB_CONNECTED;
import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS;
+import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO;
import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED;
@@ -64,8 +65,8 @@
import android.content.IntentFilter;
import android.content.res.Resources;
import android.hardware.usb.UsbManager;
+import android.net.ConnectivityManager;
import android.net.INetd;
-import android.net.INetworkPolicyManager;
import android.net.ITetheringEventCallback;
import android.net.IpPrefix;
import android.net.LinkAddress;
@@ -176,7 +177,6 @@
private final Context mContext;
private final ArrayMap<String, TetherState> mTetherStates;
private final BroadcastReceiver mStateReceiver;
- private final INetworkPolicyManager mPolicyManager;
private final Looper mLooper;
private final StateMachine mTetherMasterSM;
private final OffloadController mOffloadController;
@@ -206,12 +206,12 @@
private boolean mWifiTetherRequested;
private Network mTetherUpstream;
private TetherStatesParcel mTetherStatesParcel;
+ private boolean mDataSaverEnabled = false;
public Tethering(TetheringDependencies deps) {
mLog.mark("Tethering.constructed");
mDeps = deps;
mContext = mDeps.getContext();
- mPolicyManager = mDeps.getINetworkPolicyManager();
mNetd = mDeps.getINetd(mContext);
mLooper = mDeps.getTetheringLooper();
@@ -288,6 +288,7 @@
filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
filter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
filter.addAction(UserManager.ACTION_USER_RESTRICTIONS_CHANGED);
+ filter.addAction(ACTION_RESTRICT_BACKGROUND_CHANGED);
mContext.registerReceiver(mStateReceiver, filter, null, handler);
}
@@ -484,7 +485,7 @@
}
private void setBluetoothTethering(final boolean enable, final ResultReceiver receiver) {
- final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ final BluetoothAdapter adapter = mDeps.getBluetoothAdapter();
if (adapter == null || !adapter.isEnabled()) {
Log.w(TAG, "Tried to enable bluetooth tethering with null or disabled adapter. null: "
+ (adapter == null));
@@ -740,8 +741,7 @@
.setContentIntent(pi);
mLastNotificationId = id;
- notificationManager.notify(null, mLastNotificationId,
- mTetheredNotificationBuilder.buildInto(new Notification()));
+ notificationManager.notify(null, mLastNotificationId, mTetheredNotificationBuilder.build());
}
@VisibleForTesting
@@ -775,6 +775,9 @@
} else if (action.equals(UserManager.ACTION_USER_RESTRICTIONS_CHANGED)) {
mLog.log("OBSERVED user restrictions changed");
handleUserRestrictionAction();
+ } else if (action.equals(ACTION_RESTRICT_BACKGROUND_CHANGED)) {
+ mLog.log("OBSERVED data saver changed");
+ handleDataSaverChanged();
}
}
@@ -885,6 +888,20 @@
private void handleUserRestrictionAction() {
mTetheringRestriction.onUserRestrictionsChanged();
}
+
+ private void handleDataSaverChanged() {
+ final ConnectivityManager connMgr = (ConnectivityManager) mContext.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ final boolean isDataSaverEnabled = connMgr.getRestrictBackgroundStatus()
+ != ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
+
+ if (mDataSaverEnabled == isDataSaverEnabled) return;
+
+ mDataSaverEnabled = isDataSaverEnabled;
+ if (mDataSaverEnabled) {
+ untetherAll();
+ }
+ }
}
@VisibleForTesting
@@ -1982,15 +1999,6 @@
mLog.log(String.format("OBSERVED iface=%s state=%s error=%s", iface, state, error));
- try {
- // Notify that we're tethering (or not) this interface.
- // This is how data saver for instance knows if the user explicitly
- // turned on tethering (thus keeping us from being in data saver mode).
- mPolicyManager.onTetheringChanged(iface, state == IpServer.STATE_TETHERED);
- } catch (RemoteException e) {
- // Not really very much we can do here.
- }
-
// If TetherMasterSM is in ErrorState, TetherMasterSM stays there.
// Thus we give a chance for TetherMasterSM to recover to InitialState
// by sending CMD_CLEAR_ERROR
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
index dbe7892..068c346 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -23,18 +23,6 @@
import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
-import static com.android.internal.R.array.config_mobile_hotspot_provision_app;
-import static com.android.internal.R.array.config_tether_bluetooth_regexs;
-import static com.android.internal.R.array.config_tether_dhcp_range;
-import static com.android.internal.R.array.config_tether_upstream_types;
-import static com.android.internal.R.array.config_tether_usb_regexs;
-import static com.android.internal.R.array.config_tether_wifi_p2p_regexs;
-import static com.android.internal.R.array.config_tether_wifi_regexs;
-import static com.android.internal.R.bool.config_tether_upstream_automatic;
-import static com.android.internal.R.integer.config_mobile_hotspot_provision_check_period;
-import static com.android.internal.R.string.config_mobile_hotspot_provision_app_no_ui;
-import static com.android.networkstack.tethering.R.bool.config_tether_enable_legacy_dhcp_server;
-
import android.content.Context;
import android.content.res.Resources;
import android.net.TetheringConfigurationParcel;
@@ -45,6 +33,7 @@
import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.networkstack.tethering.R;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -113,27 +102,30 @@
activeDataSubId = id;
Resources res = getResources(ctx, activeDataSubId);
- tetherableUsbRegexs = getResourceStringArray(res, config_tether_usb_regexs);
+ tetherableUsbRegexs = getResourceStringArray(res, R.array.config_tether_usb_regexs);
// TODO: Evaluate deleting this altogether now that Wi-Fi always passes
// us an interface name. Careful consideration needs to be given to
// implications for Settings and for provisioning checks.
- tetherableWifiRegexs = getResourceStringArray(res, config_tether_wifi_regexs);
- tetherableWifiP2pRegexs = getResourceStringArray(res, config_tether_wifi_p2p_regexs);
- tetherableBluetoothRegexs = getResourceStringArray(res, config_tether_bluetooth_regexs);
+ tetherableWifiRegexs = getResourceStringArray(res, R.array.config_tether_wifi_regexs);
+ tetherableWifiP2pRegexs = getResourceStringArray(
+ res, R.array.config_tether_wifi_p2p_regexs);
+ tetherableBluetoothRegexs = getResourceStringArray(
+ res, R.array.config_tether_bluetooth_regexs);
isDunRequired = checkDunRequired(ctx);
- chooseUpstreamAutomatically = getResourceBoolean(res, config_tether_upstream_automatic);
+ chooseUpstreamAutomatically = getResourceBoolean(
+ res, R.bool.config_tether_upstream_automatic);
preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(res, isDunRequired);
legacyDhcpRanges = getLegacyDhcpRanges(res);
defaultIPv4DNS = copy(DEFAULT_IPV4_DNS);
enableLegacyDhcpServer = getEnableLegacyDhcpServer(res);
- provisioningApp = getResourceStringArray(res, config_mobile_hotspot_provision_app);
+ provisioningApp = getResourceStringArray(res, R.array.config_mobile_hotspot_provision_app);
provisioningAppNoUi = getProvisioningAppNoUi(res);
provisioningCheckPeriod = getResourceInteger(res,
- config_mobile_hotspot_provision_check_period,
+ R.integer.config_mobile_hotspot_provision_check_period,
0 /* No periodic re-check */);
configLog.log(toString());
@@ -248,7 +240,7 @@
}
private static Collection<Integer> getUpstreamIfaceTypes(Resources res, boolean dunRequired) {
- final int[] ifaceTypes = res.getIntArray(config_tether_upstream_types);
+ final int[] ifaceTypes = res.getIntArray(R.array.config_tether_upstream_types);
final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length);
for (int i : ifaceTypes) {
switch (i) {
@@ -298,7 +290,7 @@
}
private static String[] getLegacyDhcpRanges(Resources res) {
- final String[] fromResource = getResourceStringArray(res, config_tether_dhcp_range);
+ final String[] fromResource = getResourceStringArray(res, R.array.config_tether_dhcp_range);
if ((fromResource.length > 0) && (fromResource.length % 2 == 0)) {
return fromResource;
}
@@ -307,7 +299,7 @@
private static String getProvisioningAppNoUi(Resources res) {
try {
- return res.getString(config_mobile_hotspot_provision_app_no_ui);
+ return res.getString(R.string.config_mobile_hotspot_provision_app_no_ui);
} catch (Resources.NotFoundException e) {
return "";
}
@@ -339,7 +331,7 @@
}
private boolean getEnableLegacyDhcpServer(final Resources res) {
- return getResourceBoolean(res, config_tether_enable_legacy_dhcp_server)
+ return getResourceBoolean(res, R.bool.config_tether_enable_legacy_dhcp_server)
|| getDeviceConfigBoolean(TETHER_ENABLE_LEGACY_DHCP_SERVER);
}
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java
index 79e99e8..e019c3a 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -16,17 +16,15 @@
package com.android.server.connectivity.tethering;
+import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.net.INetd;
-import android.net.INetworkPolicyManager;
import android.net.NetworkRequest;
import android.net.ip.IpServer;
import android.net.util.SharedLog;
import android.os.Handler;
import android.os.IBinder;
-import android.os.INetworkManagementService;
import android.os.Looper;
-import android.os.ServiceManager;
import com.android.internal.util.StateMachine;
@@ -96,24 +94,6 @@
}
/**
- * Get a reference to INetworkManagementService to registerTetheringStatsProvider from
- * OffloadController. Note: This should be removed soon by Usage refactor work in R
- * development cycle.
- */
- public INetworkManagementService getINetworkManagementService() {
- return INetworkManagementService.Stub.asInterface(
- ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
- }
-
- /**
- * Get a reference to INetworkPolicyManager to be used by tethering.
- */
- public INetworkPolicyManager getINetworkPolicyManager() {
- return INetworkPolicyManager.Stub.asInterface(
- ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
- }
-
- /**
* Get a reference to INetd to be used by tethering.
*/
public INetd getINetd(Context context) {
@@ -130,4 +110,9 @@
* Get Context of TetheringSerice.
*/
public abstract Context getContext();
+
+ /**
+ * Get a reference to BluetoothAdapter to be used by tethering.
+ */
+ public abstract BluetoothAdapter getBluetoothAdapter();
}
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
index 99130d2..cb7d392 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
@@ -24,6 +24,7 @@
import static android.net.dhcp.IDhcpServer.STATUS_UNKNOWN_ERROR;
import android.app.Service;
+import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.Intent;
import android.net.IIntResultListener;
@@ -376,6 +377,11 @@
}
return INetworkStackConnector.Stub.asInterface(connector);
}
+
+ @Override
+ public BluetoothAdapter getBluetoothAdapter() {
+ return BluetoothAdapter.getDefaultAdapter();
+ }
};
}
return mDeps;
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
index 79bba7f..4f07461 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
@@ -27,7 +27,6 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-import static com.android.networkstack.tethering.R.bool.config_tether_enable_legacy_dhcp_server;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -56,10 +55,10 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.R;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.internal.util.test.BroadcastInterceptingContext;
+import com.android.networkstack.tethering.R;
import org.junit.After;
import org.junit.Before;
@@ -157,16 +156,18 @@
eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
when(mResources.getStringArray(R.array.config_tether_dhcp_range))
- .thenReturn(new String[0]);
+ .thenReturn(new String[0]);
when(mResources.getStringArray(R.array.config_tether_usb_regexs))
- .thenReturn(new String[0]);
+ .thenReturn(new String[0]);
when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
- .thenReturn(new String[0]);
+ .thenReturn(new String[0]);
when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs))
- .thenReturn(new String[0]);
+ .thenReturn(new String[0]);
when(mResources.getIntArray(R.array.config_tether_upstream_types))
- .thenReturn(new int[0]);
- when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false);
+ .thenReturn(new int[0]);
+ when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
+ false);
+ when(mResources.getString(R.string.config_wifi_tether_enable)).thenReturn("");
when(mLog.forSubComponent(anyString())).thenReturn(mLog);
mMockContext = new MockContext(mContext);
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
index ef97ad4..3635964 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
@@ -26,13 +26,6 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-import static com.android.internal.R.array.config_mobile_hotspot_provision_app;
-import static com.android.internal.R.array.config_tether_bluetooth_regexs;
-import static com.android.internal.R.array.config_tether_dhcp_range;
-import static com.android.internal.R.array.config_tether_upstream_types;
-import static com.android.internal.R.array.config_tether_usb_regexs;
-import static com.android.internal.R.array.config_tether_wifi_regexs;
-import static com.android.networkstack.tethering.R.bool.config_tether_enable_legacy_dhcp_server;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -51,6 +44,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.test.BroadcastInterceptingContext;
+import com.android.networkstack.tethering.R;
import org.junit.After;
import org.junit.Before;
@@ -120,15 +114,18 @@
() -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
- when(mResources.getStringArray(config_tether_dhcp_range)).thenReturn(new String[0]);
- when(mResources.getStringArray(config_tether_usb_regexs)).thenReturn(new String[0]);
- when(mResources.getStringArray(config_tether_wifi_regexs))
+ when(mResources.getStringArray(R.array.config_tether_dhcp_range)).thenReturn(
+ new String[0]);
+ when(mResources.getStringArray(R.array.config_tether_usb_regexs)).thenReturn(new String[0]);
+ when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
.thenReturn(new String[]{ "test_wlan\\d" });
- when(mResources.getStringArray(config_tether_bluetooth_regexs)).thenReturn(new String[0]);
- when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(new int[0]);
- when(mResources.getStringArray(config_mobile_hotspot_provision_app))
+ when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)).thenReturn(
+ new String[0]);
+ when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]);
+ when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
.thenReturn(new String[0]);
- when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false);
+ when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
+ false);
mHasTelephonyManager = true;
mMockContext = new MockContext(mContext);
mEnableLegacyDhcpServer = false;
@@ -140,7 +137,7 @@
}
private TetheringConfiguration getTetheringConfiguration(int... legacyTetherUpstreamTypes) {
- when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(
+ when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(
legacyTetherUpstreamTypes);
return new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
}
@@ -224,7 +221,7 @@
@Test
public void testNoDefinedUpstreamTypesAddsEthernet() {
- when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(new int[]{});
+ when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[]{});
when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false);
final TetheringConfiguration cfg = new TetheringConfiguration(
@@ -246,7 +243,7 @@
@Test
public void testDefinedUpstreamTypesSansEthernetAddsEthernet() {
- when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(
+ when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(
new int[]{TYPE_WIFI, TYPE_MOBILE_HIPRI});
when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false);
@@ -264,7 +261,7 @@
@Test
public void testDefinedUpstreamTypesWithEthernetDoesNotAddEthernet() {
- when(mResources.getIntArray(config_tether_upstream_types))
+ when(mResources.getIntArray(R.array.config_tether_upstream_types))
.thenReturn(new int[]{TYPE_WIFI, TYPE_ETHERNET, TYPE_MOBILE_HIPRI});
when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false);
@@ -282,7 +279,8 @@
@Test
public void testNewDhcpServerDisabled() {
- when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(true);
+ when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
+ true);
doReturn(false).when(
() -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
@@ -291,7 +289,8 @@
new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
assertTrue(enableByRes.enableLegacyDhcpServer);
- when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false);
+ when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
+ false);
doReturn(true).when(
() -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
@@ -303,7 +302,8 @@
@Test
public void testNewDhcpServerEnabled() {
- when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false);
+ when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
+ false);
doReturn(false).when(
() -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
@@ -329,16 +329,17 @@
private void setUpResourceForSubId() {
when(mResourcesForSubId.getStringArray(
- config_tether_dhcp_range)).thenReturn(new String[0]);
+ R.array.config_tether_dhcp_range)).thenReturn(new String[0]);
when(mResourcesForSubId.getStringArray(
- config_tether_usb_regexs)).thenReturn(new String[0]);
+ R.array.config_tether_usb_regexs)).thenReturn(new String[0]);
when(mResourcesForSubId.getStringArray(
- config_tether_wifi_regexs)).thenReturn(new String[]{ "test_wlan\\d" });
+ R.array.config_tether_wifi_regexs)).thenReturn(new String[]{ "test_wlan\\d" });
when(mResourcesForSubId.getStringArray(
- config_tether_bluetooth_regexs)).thenReturn(new String[0]);
- when(mResourcesForSubId.getIntArray(config_tether_upstream_types)).thenReturn(new int[0]);
+ R.array.config_tether_bluetooth_regexs)).thenReturn(new String[0]);
+ when(mResourcesForSubId.getIntArray(R.array.config_tether_upstream_types)).thenReturn(
+ new int[0]);
when(mResourcesForSubId.getStringArray(
- config_mobile_hotspot_provision_app)).thenReturn(PROVISIONING_APP_NAME);
+ R.array.config_mobile_hotspot_provision_app)).thenReturn(PROVISIONING_APP_NAME);
}
}
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
index 7c2c75b..5910624 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
@@ -19,6 +19,9 @@
import static android.hardware.usb.UsbManager.USB_CONFIGURED;
import static android.hardware.usb.UsbManager.USB_CONNECTED;
import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS;
+import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
import static android.net.RouteInfo.RTN_UNICAST;
import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED;
import static android.net.TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY;
@@ -37,8 +40,6 @@
import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-import static com.android.networkstack.tethering.R.bool.config_tether_enable_legacy_dhcp_server;
-
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -53,6 +54,7 @@
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
@@ -61,6 +63,7 @@
import static org.mockito.Mockito.when;
import android.app.usage.NetworkStatsManager;
+import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -69,8 +72,8 @@
import android.content.pm.ApplicationInfo;
import android.content.res.Resources;
import android.hardware.usb.UsbManager;
+import android.net.ConnectivityManager;
import android.net.INetd;
-import android.net.INetworkPolicyManager;
import android.net.ITetheringEventCallback;
import android.net.InetAddresses;
import android.net.InterfaceConfigurationParcel;
@@ -99,7 +102,6 @@
import android.net.wifi.p2p.WifiP2pManager;
import android.os.Bundle;
import android.os.Handler;
-import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.PersistableBundle;
import android.os.RemoteException;
@@ -120,6 +122,7 @@
import com.android.internal.util.StateMachine;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.networkstack.tethering.R;
import org.junit.After;
import org.junit.Before;
@@ -133,6 +136,7 @@
import java.net.Inet6Address;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Vector;
@RunWith(AndroidJUnit4.class)
@@ -151,9 +155,7 @@
@Mock private ApplicationInfo mApplicationInfo;
@Mock private Context mContext;
- @Mock private INetworkManagementService mNMService;
@Mock private NetworkStatsManager mStatsManager;
- @Mock private INetworkPolicyManager mPolicyManager;
@Mock private OffloadHardwareInterface mOffloadHardwareInterface;
@Mock private Resources mResources;
@Mock private TelephonyManager mTelephonyManager;
@@ -167,6 +169,7 @@
@Mock private INetd mNetd;
@Mock private UserManager mUserManager;
@Mock private NetworkRequest mNetworkRequest;
+ @Mock private ConnectivityManager mCm;
private final MockIpServerDependencies mIpServerDependencies =
spy(new MockIpServerDependencies());
@@ -218,6 +221,7 @@
if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager;
if (Context.USER_SERVICE.equals(name)) return mUserManager;
if (Context.NETWORK_STATS_SERVICE.equals(name)) return mStatsManager;
+ if (Context.CONNECTIVITY_SERVICE.equals(name)) return mCm;
return super.getSystemService(name);
}
@@ -335,16 +339,6 @@
}
@Override
- public INetworkManagementService getINetworkManagementService() {
- return mNMService;
- }
-
- @Override
- public INetworkPolicyManager getINetworkPolicyManager() {
- return mPolicyManager;
- }
-
- @Override
public INetd getINetd(Context context) {
return mNetd;
}
@@ -358,6 +352,12 @@
public Context getContext() {
return mServiceContext;
}
+
+ @Override
+ public BluetoothAdapter getBluetoothAdapter() {
+ // TODO: add test for bluetooth tethering.
+ return null;
+ }
}
private static UpstreamNetworkState buildMobileUpstreamState(boolean withIPv4,
@@ -416,24 +416,24 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- when(mResources.getStringArray(com.android.internal.R.array.config_tether_dhcp_range))
+ when(mResources.getStringArray(R.array.config_tether_dhcp_range))
.thenReturn(new String[0]);
- when(mResources.getStringArray(com.android.internal.R.array.config_tether_usb_regexs))
+ when(mResources.getStringArray(R.array.config_tether_usb_regexs))
.thenReturn(new String[] { "test_rndis\\d" });
- when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_regexs))
+ when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
.thenReturn(new String[]{ "test_wlan\\d" });
- when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_p2p_regexs))
+ when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs))
.thenReturn(new String[]{ "test_p2p-p2p\\d-.*" });
- when(mResources.getStringArray(com.android.internal.R.array.config_tether_bluetooth_regexs))
+ when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs))
.thenReturn(new String[0]);
- when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types))
- .thenReturn(new int[0]);
- when(mResources.getBoolean(com.android.internal.R.bool.config_tether_upstream_automatic))
- .thenReturn(false);
- when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false);
+ when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]);
+ when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(false);
+ when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
+ false);
when(mNetd.interfaceGetList())
.thenReturn(new String[] {
TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME});
+ when(mResources.getString(R.string.config_wifi_tether_enable)).thenReturn("");
mInterfaceConfiguration = new InterfaceConfigurationParcel();
mInterfaceConfiguration.flags = new String[0];
when(mRouterAdvertisementDaemon.start())
@@ -696,7 +696,8 @@
@Test
public void workingMobileUsbTethering_IPv4LegacyDhcp() {
- when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(true);
+ when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
+ true);
sendConfigurationChanged();
final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
runUsbTethering(upstreamState);
@@ -784,8 +785,7 @@
@Test
public void configTetherUpstreamAutomaticIgnoresConfigTetherUpstreamTypes() throws Exception {
- when(mResources.getBoolean(com.android.internal.R.bool.config_tether_upstream_automatic))
- .thenReturn(true);
+ when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(true);
sendConfigurationChanged();
// Setup IPv6
@@ -1313,7 +1313,7 @@
private void workingWifiP2pGroupOwnerLegacyMode(
boolean emulateInterfaceStatusChanged) throws Exception {
// change to legacy mode and update tethering information by chaning SIM
- when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_p2p_regexs))
+ when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs))
.thenReturn(new String[]{});
final int fakeSubId = 1234;
mPhoneStateListener.onActiveDataSubscriptionIdChanged(fakeSubId);
@@ -1351,6 +1351,50 @@
workingWifiP2pGroupClient(false);
}
+ private void setDataSaverEnabled(boolean enabled) {
+ final Intent intent = new Intent(ACTION_RESTRICT_BACKGROUND_CHANGED);
+ mServiceContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+
+ final int status = enabled ? RESTRICT_BACKGROUND_STATUS_ENABLED
+ : RESTRICT_BACKGROUND_STATUS_DISABLED;
+ when(mCm.getRestrictBackgroundStatus()).thenReturn(status);
+ mLooper.dispatchAll();
+ }
+
+ @Test
+ public void testDataSaverChanged() {
+ // Start Tethering.
+ final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
+ runUsbTethering(upstreamState);
+ assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME);
+ // Data saver is ON.
+ setDataSaverEnabled(true);
+ // Verify that tethering should be disabled.
+ verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NONE);
+ mTethering.interfaceRemoved(TEST_USB_IFNAME);
+ mLooper.dispatchAll();
+ assertEquals(mTethering.getTetheredIfaces(), new String[0]);
+ reset(mUsbManager);
+
+ runUsbTethering(upstreamState);
+ // Verify that user can start tethering again without turning OFF data saver.
+ assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME);
+
+ // If data saver is keep ON with change event, tethering should not be OFF this time.
+ setDataSaverEnabled(true);
+ verify(mUsbManager, times(0)).setCurrentFunctions(UsbManager.FUNCTION_NONE);
+ assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME);
+
+ // If data saver is turned OFF, it should not change tethering.
+ setDataSaverEnabled(false);
+ verify(mUsbManager, times(0)).setCurrentFunctions(UsbManager.FUNCTION_NONE);
+ assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME);
+ }
+
+ private static <T> void assertContains(Collection<T> collection, T element) {
+ assertTrue(element + " not found in " + collection, collection.contains(element));
+ }
+
// TODO: Test that a request for hotspot mode doesn't interfere with an
// already operating tethering mode interface.
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 0160286..c3965c4 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -36,8 +36,12 @@
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
+import android.graphics.Bitmap;
+import android.graphics.Point;
+import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerGlobal;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -54,6 +58,7 @@
import android.view.Display;
import android.view.KeyEvent;
import android.view.MagnificationSpec;
+import android.view.SurfaceControl;
import android.view.View;
import android.view.WindowInfo;
import android.view.accessibility.AccessibilityCache;
@@ -90,6 +95,7 @@
private static final String LOG_TAG = "AbstractAccessibilityServiceConnection";
private static final int WAIT_WINDOWS_TIMEOUT_MILLIS = 5000;
+ protected static final String TAKE_SCREENSHOT = "takeScreenshot";
protected final Context mContext;
protected final SystemSupport mSystemSupport;
protected final WindowManagerInternal mWindowManagerService;
@@ -934,6 +940,54 @@
mInvocationHandler.setSoftKeyboardCallbackEnabled(enabled);
}
+ @Nullable
+ @Override
+ public Bitmap takeScreenshot(int displayId) {
+ synchronized (mLock) {
+ if (!hasRightsToCurrentUserLocked()) {
+ return null;
+ }
+
+ if (!mSecurityPolicy.canTakeScreenshotLocked(this)) {
+ return null;
+ }
+ }
+
+ if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
+ return null;
+ }
+
+ final Display display = DisplayManagerGlobal.getInstance()
+ .getRealDisplay(displayId);
+ if (display == null) {
+ return null;
+ }
+ final Point displaySize = new Point();
+ display.getRealSize(displaySize);
+
+ final int rotation = display.getRotation();
+ Bitmap screenShot = null;
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ final Rect crop = new Rect(0, 0, displaySize.x, displaySize.y);
+ // TODO (b/145893483): calling new API with the display as a parameter
+ // when surface control supported.
+ screenShot = SurfaceControl.screenshot(crop, displaySize.x, displaySize.y,
+ rotation);
+ if (screenShot != null) {
+ // Optimization for telling the bitmap that all of the pixels are known to be
+ // opaque (false). This is meant as a drawing hint, as in some cases a bitmap
+ // that is known to be opaque can take a faster drawing case than one that may
+ // have non-opaque per-pixel alpha values.
+ screenShot.setHasAlpha(false);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ return screenShot;
+ }
+
@Override
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 58d3489..36a9e0eb 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -411,6 +411,7 @@
if (reboundAService) {
onUserStateChangedLocked(userState);
}
+ migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, packageName);
}
}
@@ -811,9 +812,7 @@
userState.setTouchExplorationEnabledLocked(touchExplorationEnabled);
userState.setDisplayMagnificationEnabledLocked(false);
- userState.setNavBarMagnificationEnabledLocked(false);
userState.disableShortcutMagnificationLocked();
-
userState.setAutoclickEnabledLocked(false);
userState.mEnabledServices.clear();
userState.mEnabledServices.add(service);
@@ -853,17 +852,20 @@
* navigation area has been clicked.
*
* @param displayId The logical display id.
+ * @param targetName The flattened {@link ComponentName} string or the class name of a system
+ * class implementing a supported accessibility feature, or {@code null} if there's no
+ * specified target.
*/
@Override
- public void notifyAccessibilityButtonClicked(int displayId) {
+ public void notifyAccessibilityButtonClicked(int displayId, String targetName) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR_SERVICE)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Caller does not hold permission "
+ android.Manifest.permission.STATUS_BAR_SERVICE);
}
- synchronized (mLock) {
- notifyAccessibilityButtonClickedLocked(displayId);
- }
+ mMainHandler.sendMessage(obtainMessage(
+ AccessibilityManagerService::performAccessibilityShortcutInternal, this,
+ displayId, ACCESSIBILITY_BUTTON, targetName));
}
/**
@@ -1023,6 +1025,7 @@
// the state since the context in which the current user
// state was used has changed since it was inactive.
onUserStateChangedLocked(userState);
+ migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, null);
if (announceNewUser) {
// Schedule announcement of the current user if needed.
@@ -1132,66 +1135,6 @@
}
}
- // TODO(a11y shortcut): Remove this function and Use #performAccessibilityShortcutInternal(
- // ACCESSIBILITY_BUTTON) instead, after the new Settings shortcut Ui merged.
- private void notifyAccessibilityButtonClickedLocked(int displayId) {
- final AccessibilityUserState state = getCurrentUserStateLocked();
-
- int potentialTargets = state.isNavBarMagnificationEnabledLocked() ? 1 : 0;
- for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
- final AccessibilityServiceConnection service = state.mBoundServices.get(i);
- if (service.mRequestAccessibilityButton) {
- potentialTargets++;
- }
- }
-
- if (potentialTargets == 0) {
- return;
- }
- if (potentialTargets == 1) {
- if (state.isNavBarMagnificationEnabledLocked()) {
- mMainHandler.sendMessage(obtainMessage(
- AccessibilityManagerService::sendAccessibilityButtonToInputFilter, this,
- displayId));
- return;
- } else {
- for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
- final AccessibilityServiceConnection service = state.mBoundServices.get(i);
- if (service.mRequestAccessibilityButton) {
- service.notifyAccessibilityButtonClickedLocked(displayId);
- return;
- }
- }
- }
- } else {
- if (state.getServiceAssignedToAccessibilityButtonLocked() == null
- && !state.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
- mMainHandler.sendMessage(obtainMessage(
- AccessibilityManagerService::showAccessibilityTargetsSelection, this,
- displayId, ACCESSIBILITY_BUTTON));
- } else if (state.isNavBarMagnificationEnabledLocked()
- && state.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
- mMainHandler.sendMessage(obtainMessage(
- AccessibilityManagerService::sendAccessibilityButtonToInputFilter, this,
- displayId));
- return;
- } else {
- for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
- final AccessibilityServiceConnection service = state.mBoundServices.get(i);
- if (service.mRequestAccessibilityButton && (service.mComponentName.equals(
- state.getServiceAssignedToAccessibilityButtonLocked()))) {
- service.notifyAccessibilityButtonClickedLocked(displayId);
- return;
- }
- }
- }
- // The user may have turned off the assigned service or feature
- mMainHandler.sendMessage(obtainMessage(
- AccessibilityManagerService::showAccessibilityTargetsSelection, this,
- displayId, ACCESSIBILITY_BUTTON));
- }
- }
-
private void sendAccessibilityButtonToInputFilter(int displayId) {
synchronized (mLock) {
if (mHasInputFilter && mInputFilter != null) {
@@ -1635,8 +1578,7 @@
if (userState.isDisplayMagnificationEnabledLocked()) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_SCREEN_MAGNIFIER;
}
- if (userState.isNavBarMagnificationEnabledLocked()
- || userState.isShortcutKeyMagnificationEnabledLocked()) {
+ if (userState.isShortcutMagnificationEnabledLocked()) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER;
}
if (userHasMagnificationServicesLocked(userState)) {
@@ -1886,14 +1828,8 @@
mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
0, userState.mUserId) == 1;
- final boolean navBarMagnificationEnabled = Settings.Secure.getIntForUser(
- mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
- 0, userState.mUserId) == 1;
- if ((displayMagnificationEnabled != userState.isDisplayMagnificationEnabledLocked())
- || (navBarMagnificationEnabled != userState.isNavBarMagnificationEnabledLocked())) {
+ if ((displayMagnificationEnabled != userState.isDisplayMagnificationEnabledLocked())) {
userState.setDisplayMagnificationEnabledLocked(displayMagnificationEnabled);
- userState.setNavBarMagnificationEnabledLocked(navBarMagnificationEnabled);
return true;
}
return false;
@@ -2088,8 +2024,7 @@
// displays in one display. It's not a real display and there's no input events for it.
final ArrayList<Display> displays = getValidDisplayList();
if (userState.isDisplayMagnificationEnabledLocked()
- || userState.isNavBarMagnificationEnabledLocked()
- || userState.isShortcutKeyMagnificationEnabledLocked()) {
+ || userState.isShortcutMagnificationEnabledLocked()) {
for (int i = 0; i < displays.size(); i++) {
final Display display = displays.get(i);
getMagnificationController().register(display.getDisplayId());
@@ -2208,6 +2143,85 @@
scheduleNotifyClientsOfServicesStateChangeLocked(userState);
}
+ /**
+ * 1) Check if the service assigned to accessibility button target sdk version > Q.
+ * If it isn't, remove it from the list and associated setting.
+ * (It happens when an accessibility service package is downgraded.)
+ * 2) Check if an enabled service targeting sdk version > Q and requesting a11y button is
+ * assigned to a shortcut. If it isn't, assigns it to the accessibility button.
+ * (It happens when an enabled accessibility service package is upgraded.)
+ *
+ * @param packageName The package name to check, or {@code null} to check all services.
+ */
+ private void migrateAccessibilityButtonSettingsIfNecessaryLocked(
+ AccessibilityUserState userState, @Nullable String packageName) {
+ final Set<String> buttonTargets =
+ userState.getShortcutTargetsLocked(ACCESSIBILITY_BUTTON);
+ int lastSize = buttonTargets.size();
+ buttonTargets.removeIf(name -> {
+ if (packageName != null && name != null && !name.contains(packageName)) {
+ return false;
+ }
+ final ComponentName componentName = ComponentName.unflattenFromString(name);
+ if (componentName == null) {
+ return false;
+ }
+ final AccessibilityServiceInfo serviceInfo =
+ userState.getInstalledServiceInfoLocked(componentName);
+ if (serviceInfo == null) {
+ return false;
+ }
+ if (serviceInfo.getResolveInfo().serviceInfo.applicationInfo
+ .targetSdkVersion > Build.VERSION_CODES.Q) {
+ return false;
+ }
+ // A11y services targeting sdk version <= Q should not be in the list.
+ return true;
+ });
+ boolean changed = (lastSize != buttonTargets.size());
+ lastSize = buttonTargets.size();
+
+ final Set<String> shortcutKeyTargets =
+ userState.getShortcutTargetsLocked(ACCESSIBILITY_SHORTCUT_KEY);
+ userState.mEnabledServices.forEach(componentName -> {
+ if (packageName != null && componentName != null
+ && !packageName.equals(componentName.getPackageName())) {
+ return;
+ }
+ final AccessibilityServiceInfo serviceInfo =
+ userState.getInstalledServiceInfoLocked(componentName);
+ if (serviceInfo == null) {
+ return;
+ }
+ final boolean requestA11yButton = (serviceInfo.flags
+ & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
+ if (!(serviceInfo.getResolveInfo().serviceInfo.applicationInfo
+ .targetSdkVersion > Build.VERSION_CODES.Q && requestA11yButton)) {
+ return;
+ }
+ final String serviceName = serviceInfo.getComponentName().flattenToString();
+ if (TextUtils.isEmpty(serviceName)) {
+ return;
+ }
+ if (shortcutKeyTargets.contains(serviceName) || buttonTargets.contains(serviceName)) {
+ return;
+ }
+ // For enabled a11y services targeting sdk version > Q and requesting a11y button should
+ // be assigned to a shortcut.
+ buttonTargets.add(serviceName);
+ });
+ changed |= (lastSize != buttonTargets.size());
+ if (!changed) {
+ return;
+ }
+
+ // Update setting key with new value.
+ persistColonDelimitedSetToSettingLocked(
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
+ userState.mUserId, buttonTargets, str -> str);
+ scheduleNotifyClientsOfServicesStateChangeLocked(userState);
+ }
+
private void updateRecommendedUiTimeoutLocked(AccessibilityUserState userState) {
int newNonInteractiveUiTimeout = userState.getUserNonInteractiveUiTimeoutLocked();
int newInteractiveUiTimeout = userState.getUserInteractiveUiTimeoutLocked();
@@ -2269,9 +2283,13 @@
* AIDL-exposed method to be called when the accessibility shortcut key is enabled. Requires
* permission to write secure settings, since someone with that permission can enable
* accessibility services themselves.
+ *
+ * @param targetName The flattened {@link ComponentName} string or the class name of a system
+ * class implementing a supported accessibility feature, or {@code null} if there's no
+ * specified target.
*/
@Override
- public void performAccessibilityShortcut() {
+ public void performAccessibilityShortcut(String targetName) {
if ((UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID)
&& (mContext.checkCallingPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
!= PackageManager.PERMISSION_GRANTED)) {
@@ -2280,7 +2298,7 @@
}
mMainHandler.sendMessage(obtainMessage(
AccessibilityManagerService::performAccessibilityShortcutInternal, this,
- Display.DEFAULT_DISPLAY, ACCESSIBILITY_SHORTCUT_KEY));
+ Display.DEFAULT_DISPLAY, ACCESSIBILITY_SHORTCUT_KEY, targetName));
}
/**
@@ -2288,20 +2306,31 @@
*
* @param shortcutType The shortcut type.
* @param displayId The display id of the accessibility button.
+ * @param targetName The flattened {@link ComponentName} string or the class name of a system
+ * class implementing a supported accessibility feature, or {@code null} if there's no
+ * specified target.
*/
private void performAccessibilityShortcutInternal(int displayId,
- @ShortcutType int shortcutType) {
+ @ShortcutType int shortcutType, @Nullable String targetName) {
final List<String> shortcutTargets = getAccessibilityShortcutTargetsInternal(shortcutType);
if (shortcutTargets.isEmpty()) {
Slog.d(LOG_TAG, "No target to perform shortcut, shortcutType=" + shortcutType);
return;
}
- // In case there are many targets assigned to the given shortcut.
- if (shortcutTargets.size() > 1) {
- showAccessibilityTargetsSelection(displayId, shortcutType);
- return;
+ // In case the caller specified a target name
+ if (targetName != null) {
+ if (!shortcutTargets.contains(targetName)) {
+ Slog.d(LOG_TAG, "Perform shortcut failed, invalid target name:" + targetName);
+ return;
+ }
+ } else {
+ // In case there are many targets assigned to the given shortcut.
+ if (shortcutTargets.size() > 1) {
+ showAccessibilityTargetsSelection(displayId, shortcutType);
+ return;
+ }
+ targetName = shortcutTargets.get(0);
}
- final String targetName = shortcutTargets.get(0);
// In case user assigned magnification to the given shortcut.
if (targetName.equals(MAGNIFICATION_CONTROLLER_NAME)) {
sendAccessibilityButtonToInputFilter(displayId);
@@ -2844,11 +2873,6 @@
private final Uri mDisplayMagnificationEnabledUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED);
- // TODO(a11y shortcut): Remove this setting key, and have a migrate function in
- // Setting provider after new shortcut UI merged.
- private final Uri mNavBarMagnificationEnabledUri = Settings.Secure.getUriFor(
- Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
-
private final Uri mAutoclickEnabledUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED);
@@ -2888,8 +2912,6 @@
false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(mDisplayMagnificationEnabledUri,
false, this, UserHandle.USER_ALL);
- contentResolver.registerContentObserver(mNavBarMagnificationEnabledUri,
- false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(mAutoclickEnabledUri,
false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(mEnabledAccessibilityServicesUri,
@@ -2924,8 +2946,7 @@
if (readTouchExplorationEnabledSettingLocked(userState)) {
onUserStateChangedLocked(userState);
}
- } else if (mDisplayMagnificationEnabledUri.equals(uri)
- || mNavBarMagnificationEnabledUri.equals(uri)) {
+ } else if (mDisplayMagnificationEnabledUri.equals(uri)) {
if (readMagnificationEnabledSettingsLocked(userState)) {
onUserStateChangedLocked(userState);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
index 0151a52..7dbec7c 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
@@ -326,6 +326,19 @@
}
/**
+ * Checks if a service can take screenshot.
+ *
+ * @param service The service requesting access
+ *
+ * @return Whether ot not the service may take screenshot
+ */
+ public boolean canTakeScreenshotLocked(
+ @NonNull AbstractAccessibilityServiceConnection service) {
+ return (service.getCapabilities()
+ & AccessibilityServiceInfo.CAPABILITY_CAN_TAKE_SCREENSHOT) != 0;
+ }
+
+ /**
* Returns the parent userId of the profile according to the specified userId.
*
* @param userId The userId to check
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 37ac3ec..25911a7 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -16,6 +16,8 @@
package com.android.server.accessibility;
+import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT;
+
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import android.Manifest;
@@ -25,16 +27,20 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
+import android.graphics.Bitmap;
import android.os.Binder;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Process;
+import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
import android.view.Display;
+import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -313,48 +319,16 @@
}
}
- // TODO(a11y shortcut): Refactoring the logic here, after the new Settings shortcut Ui merged.
public boolean isAccessibilityButtonAvailableLocked(AccessibilityUserState userState) {
// If the service does not request the accessibility button, it isn't available
if (!mRequestAccessibilityButton) {
return false;
}
-
// If the accessibility button isn't currently shown, it cannot be available to services
if (!mSystemSupport.isAccessibilityButtonShown()) {
return false;
}
-
- // If magnification is on and assigned to the accessibility button, services cannot be
- if (userState.isNavBarMagnificationEnabledLocked()
- && userState.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
- return false;
- }
-
- int requestingServices = 0;
- for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
- final AccessibilityServiceConnection service = userState.mBoundServices.get(i);
- if (service.mRequestAccessibilityButton) {
- requestingServices++;
- }
- }
-
- if (requestingServices == 1) {
- // If only a single service is requesting, it must be this service, and the
- // accessibility button is available to it
- return true;
- } else {
- // With more than one active service, we derive the target from the user's settings
- if (userState.getServiceAssignedToAccessibilityButtonLocked() == null) {
- // If the user has not made an assignment, we treat the button as available to
- // all services until the user interacts with the button to make an assignment
- return true;
- } else {
- // If an assignment was made, it defines availability
- return mComponentName.equals(
- userState.getServiceAssignedToAccessibilityButtonLocked());
- }
- }
+ return true;
}
@Override
@@ -419,4 +393,15 @@
}
}
}
+
+ @Override
+ public void takeScreenshotWithCallback(int displayId, RemoteCallback callback) {
+ mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> {
+ final Bitmap screenshot = super.takeScreenshot(displayId);
+ // Send back the result.
+ final Bundle payload = new Bundle();
+ payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT, screenshot);
+ callback.sendResult(payload);
+ }, null).recycleOnUse());
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index a163f74..ebe2af6 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -100,7 +100,6 @@
private boolean mIsAutoclickEnabled;
private boolean mIsDisplayMagnificationEnabled;
private boolean mIsFilterKeyEventsEnabled;
- private boolean mIsNavBarMagnificationEnabled;
private boolean mIsPerformGesturesEnabled;
private boolean mIsTextHighContrastEnabled;
private boolean mIsTouchExplorationEnabled;
@@ -153,7 +152,6 @@
mAccessibilityButtonTargets.clear();
mIsTouchExplorationEnabled = false;
mIsDisplayMagnificationEnabled = false;
- mIsNavBarMagnificationEnabled = false;
mIsAutoclickEnabled = false;
mUserNonInteractiveUiTimeout = 0;
mUserInteractiveUiTimeout = 0;
@@ -435,8 +433,6 @@
pw.append(", touchExplorationEnabled=").append(String.valueOf(mIsTouchExplorationEnabled));
pw.append(", displayMagnificationEnabled=").append(String.valueOf(
mIsDisplayMagnificationEnabled));
- pw.append(", navBarMagnificationEnabled=").append(String.valueOf(
- mIsNavBarMagnificationEnabled));
pw.append(", autoclickEnabled=").append(String.valueOf(mIsAutoclickEnabled));
pw.append(", nonInteractiveUiTimeout=").append(String.valueOf(mNonInteractiveUiTimeout));
pw.append(", interactiveUiTimeout=").append(String.valueOf(mInteractiveUiTimeout));
@@ -553,8 +549,12 @@
mLastSentClientState = state;
}
- public boolean isShortcutKeyMagnificationEnabledLocked() {
- return mAccessibilityShortcutKeyTargets.contains(MAGNIFICATION_CONTROLLER_NAME);
+ /**
+ * Returns true if navibar magnification or shortcut key magnification is enabled.
+ */
+ public boolean isShortcutMagnificationEnabledLocked() {
+ return mAccessibilityShortcutKeyTargets.contains(MAGNIFICATION_CONTROLLER_NAME)
+ || mAccessibilityButtonTargets.contains(MAGNIFICATION_CONTROLLER_NAME);
}
/**
@@ -690,28 +690,4 @@
public void setUserNonInteractiveUiTimeoutLocked(int timeout) {
mUserNonInteractiveUiTimeout = timeout;
}
-
- // TODO(a11y shortcut): These functions aren't necessary, after the new Settings shortcut Ui
- // is merged.
- boolean isNavBarMagnificationEnabledLocked() {
- return mIsNavBarMagnificationEnabled;
- }
-
- void setNavBarMagnificationEnabledLocked(boolean enabled) {
- mIsNavBarMagnificationEnabled = enabled;
- }
-
- boolean isNavBarMagnificationAssignedToAccessibilityButtonLocked() {
- return mAccessibilityButtonTargets.contains(MAGNIFICATION_CONTROLLER_NAME);
- }
-
- ComponentName getServiceAssignedToAccessibilityButtonLocked() {
- final String targetName = mAccessibilityButtonTargets.isEmpty() ? null
- : mAccessibilityButtonTargets.valueAt(0);
- if (targetName == null) {
- return null;
- }
- return ComponentName.unflattenFromString(targetName);
- }
- // TODO(a11y shortcut): End
}
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index 7a8a112..5d9af26 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -25,6 +25,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
+import android.os.RemoteCallback;
import android.os.RemoteException;
import android.util.Slog;
import android.view.Display;
@@ -325,5 +326,8 @@
@Override
public void onFingerprintGesture(int gesture) {}
+
+ @Override
+ public void takeScreenshotWithCallback(int displayId, RemoteCallback callback) {}
}
}
diff --git a/services/art-profile b/services/art-profile
index a0338d5..4e113c8 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -3066,18 +3066,18 @@
HSPLcom/android/server/am/AppBindRecord;->dumpInIntentBind(Ljava/io/PrintWriter;Ljava/lang/String;)V
PLcom/android/server/am/AppBindRecord;->toString()Ljava/lang/String;
PLcom/android/server/am/AppBindRecord;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V
-PLcom/android/server/am/AppCompactor$1;->onPropertyChanged(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-HSPLcom/android/server/am/AppCompactor$MemCompactionHandler;->handleMessage(Landroid/os/Message;)V
-HSPLcom/android/server/am/AppCompactor;-><init>(Lcom/android/server/am/ActivityManagerService;)V
-HSPLcom/android/server/am/AppCompactor;->access$1000(Lcom/android/server/am/AppCompactor;)V
-HSPLcom/android/server/am/AppCompactor;->access$700(Lcom/android/server/am/AppCompactor;)Lcom/android/server/am/ActivityManagerService;
-HSPLcom/android/server/am/AppCompactor;->access$800(Lcom/android/server/am/AppCompactor;)Ljava/util/ArrayList;
-HSPLcom/android/server/am/AppCompactor;->access$900(Lcom/android/server/am/AppCompactor;)Ljava/util/Random;
-PLcom/android/server/am/AppCompactor;->dump(Ljava/io/PrintWriter;)V
-HSPLcom/android/server/am/AppCompactor;->init()V
-HSPLcom/android/server/am/AppCompactor;->updateCompactionThrottles()V
-HSPLcom/android/server/am/AppCompactor;->updateUseCompaction()V
-HSPLcom/android/server/am/AppCompactor;->useCompaction()Z
+PLcom/android/server/am/CachedAppOptimizer$1;->onPropertyChanged(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+HSPLcom/android/server/am/CachedAppOptimizer$MemCompactionHandler;->handleMessage(Landroid/os/Message;)V
+HSPLcom/android/server/am/CachedAppOptimizer;-><init>(Lcom/android/server/am/ActivityManagerService;)V
+HSPLcom/android/server/am/CachedAppOptimizer;->access$1000(Lcom/android/server/am/CachedAppOptimizer;)V
+HSPLcom/android/server/am/CachedAppOptimizer;->access$700(Lcom/android/server/am/CachedAppOptimizer;)Lcom/android/server/am/ActivityManagerService;
+HSPLcom/android/server/am/CachedAppOptimizer;->access$800(Lcom/android/server/am/CachedAppOptimizer;)Ljava/util/ArrayList;
+HSPLcom/android/server/am/CachedAppOptimizer;->access$900(Lcom/android/server/am/CachedAppOptimizer;)Ljava/util/Random;
+PLcom/android/server/am/CachedAppOptimizer;->dump(Ljava/io/PrintWriter;)V
+HSPLcom/android/server/am/CachedAppOptimizer;->init()V
+HSPLcom/android/server/am/CachedAppOptimizer;->updateCompactionThrottles()V
+HSPLcom/android/server/am/CachedAppOptimizer;->updateUseCompaction()V
+HSPLcom/android/server/am/CachedAppOptimizer;->useCompaction()Z
PLcom/android/server/am/AppErrorDialog$1;->handleMessage(Landroid/os/Message;)V
PLcom/android/server/am/AppErrorDialog;-><init>(Landroid/content/Context;Lcom/android/server/am/ActivityManagerService;Lcom/android/server/am/AppErrorDialog$Data;)V
PLcom/android/server/am/AppErrorDialog;->onClick(Landroid/view/View;)V
@@ -18632,9 +18632,9 @@
Lcom/android/server/am/ActivityManagerService$UidObserverRegistration;
Lcom/android/server/am/ActivityManagerService;
Lcom/android/server/am/AppBindRecord;
-Lcom/android/server/am/AppCompactor$1;
-Lcom/android/server/am/AppCompactor$MemCompactionHandler;
-Lcom/android/server/am/AppCompactor;
+Lcom/android/server/am/CachedAppOptimizer$1;
+Lcom/android/server/am/CachedAppOptimizer$MemCompactionHandler;
+Lcom/android/server/am/CachedAppOptimizer;
Lcom/android/server/am/AppErrorDialog$Data;
Lcom/android/server/am/AppErrorResult;
Lcom/android/server/am/AppErrors$BadProcessInfo;
diff --git a/services/art-profile-boot b/services/art-profile-boot
index e09424b..fe4178a 100644
--- a/services/art-profile-boot
+++ b/services/art-profile-boot
@@ -538,7 +538,7 @@
Lcom/android/server/am/ActivityManagerService;->updateLowMemStateLocked(III)Z
Lcom/android/server/wm/ConfigurationContainer;->getWindowConfiguration()Landroid/app/WindowConfiguration;
Lcom/android/server/am/OomAdjuster;->applyOomAdjLocked(Lcom/android/server/am/ProcessRecord;ZJJ)Z
-Lcom/android/server/am/AppCompactor;->useCompaction()Z
+Lcom/android/server/am/CachedAppOptimizer;->useCompaction()Z
Lcom/android/server/am/ProcessList;->procStatesDifferForMem(II)Z
Lcom/android/server/am/ActivityManagerService;->dispatchUidsChanged()V
Lcom/android/server/audio/AudioService$VolumeStreamState;->setIndex(IILjava/lang/String;)Z
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 7b95ab5..2c229b4 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -2538,7 +2538,6 @@
KeyValueBackupJob.schedule(mUserId, mContext, mConstants);
} else {
if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
- synchronized (mQueueLock) {
// Fire the intent that kicks off the whole shebang...
try {
mRunBackupIntent.send();
@@ -2546,10 +2545,8 @@
// should never happen
Slog.e(TAG, "run-backup intent cancelled!");
}
-
// ...and cancel any pending scheduled job, because we've just superseded it
KeyValueBackupJob.cancel(mUserId, mContext);
- }
}
} finally {
Binder.restoreCallingIdentity(oldId);
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index c0f43a8..e7f7846 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -25,11 +25,14 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManagerInternal;
+import android.util.ArrayMap;
import android.util.Slog;
import com.android.server.SystemService.TargetUser;
import com.android.server.utils.TimingsTraceAndSlog;
+import dalvik.system.PathClassLoader;
+
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
@@ -63,6 +66,9 @@
// Services that should receive lifecycle events.
private final ArrayList<SystemService> mServices = new ArrayList<SystemService>();
+ // Map of paths to PathClassLoader, so we don't load the same path multiple times.
+ private final ArrayMap<String, PathClassLoader> mLoadedPaths = new ArrayMap<>();
+
private int mCurrentPhase = -1;
private UserManagerInternal mUserManagerInternal;
@@ -76,20 +82,46 @@
*
* @return The service instance.
*/
- @SuppressWarnings("unchecked")
public SystemService startService(String className) {
- final Class<SystemService> serviceClass;
+ final Class<SystemService> serviceClass = loadClassFromLoader(className,
+ this.getClass().getClassLoader());
+ return startService(serviceClass);
+ }
+
+ /**
+ * Starts a service by class name and a path that specifies the jar where the service lives.
+ *
+ * @return The service instance.
+ */
+ public SystemService startServiceFromJar(String className, String path) {
+ PathClassLoader pathClassLoader = mLoadedPaths.get(path);
+ if (pathClassLoader == null) {
+ // NB: the parent class loader should always be the system server class loader.
+ // Changing it has implications that require discussion with the mainline team.
+ pathClassLoader = new PathClassLoader(path, this.getClass().getClassLoader());
+ mLoadedPaths.put(path, pathClassLoader);
+ }
+ final Class<SystemService> serviceClass = loadClassFromLoader(className, pathClassLoader);
+ return startService(serviceClass);
+ }
+
+ /*
+ * Loads and initializes a class from the given classLoader. Returns the class.
+ */
+ @SuppressWarnings("unchecked")
+ private static Class<SystemService> loadClassFromLoader(String className,
+ ClassLoader classLoader) {
try {
- serviceClass = (Class<SystemService>)Class.forName(className);
+ return (Class<SystemService>) Class.forName(className, true, classLoader);
} catch (ClassNotFoundException ex) {
- Slog.i(TAG, "Starting " + className);
throw new RuntimeException("Failed to create service " + className
- + ": service class not found, usually indicates that the caller should "
+ + " from class loader " + classLoader.toString() + ": service class not "
+ + "found, usually indicates that the caller should "
+ "have called PackageManager.hasSystemFeature() to check whether the "
+ "feature is available on this device before trying to start the "
- + "services that implement it", ex);
+ + "services that implement it. Also ensure that the correct path for the "
+ + "classloader is supplied, if applicable.", ex);
}
- return startService(serviceClass);
}
/**
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 27e0d52..5a56a9f 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -61,7 +61,6 @@
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.util.DebugUtils;
-import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.StatsLog;
@@ -164,8 +163,7 @@
private int mHapticFeedbackIntensity;
private int mNotificationIntensity;
private int mRingIntensity;
- private SparseArray<Pair<VibrationEffect, VibrationAttributes>> mAlwaysOnEffects =
- new SparseArray<>();
+ private SparseArray<Vibration> mAlwaysOnEffects = new SparseArray<>();
static native boolean vibratorExists();
static native void vibratorInit();
@@ -461,6 +459,10 @@
Settings.System.getUriFor(Settings.System.RING_VIBRATION_INTENSITY),
true, mSettingObserver, UserHandle.USER_ALL);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.ZEN_MODE),
+ true, mSettingObserver, UserHandle.USER_ALL);
+
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -508,7 +510,8 @@
}
@Override // Binder call
- public boolean setAlwaysOnEffect(int id, VibrationEffect effect, VibrationAttributes attrs) {
+ public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, VibrationEffect effect,
+ VibrationAttributes attrs) {
if (!hasPermission(android.Manifest.permission.VIBRATE_ALWAYS_ON)) {
throw new SecurityException("Requires VIBRATE_ALWAYS_ON permission");
}
@@ -518,8 +521,8 @@
}
if (effect == null) {
synchronized (mLock) {
- mAlwaysOnEffects.delete(id);
- vibratorAlwaysOnDisable(id);
+ mAlwaysOnEffects.delete(alwaysOnId);
+ vibratorAlwaysOnDisable(alwaysOnId);
}
} else {
if (!verifyVibrationEffect(effect)) {
@@ -529,13 +532,11 @@
Slog.e(TAG, "Only prebaked effects supported for always-on.");
return false;
}
- if (attrs == null) {
- attrs = new VibrationAttributes.Builder()
- .build();
- }
+ attrs = fixupVibrationAttributes(attrs);
synchronized (mLock) {
- mAlwaysOnEffects.put(id, Pair.create(effect, attrs));
- updateAlwaysOnLocked(id, effect, attrs);
+ Vibration vib = new Vibration(null, effect, attrs, uid, opPkg, null);
+ mAlwaysOnEffects.put(alwaysOnId, vib);
+ updateAlwaysOnLocked(alwaysOnId, vib);
}
}
return true;
@@ -575,6 +576,23 @@
return true;
}
+ private VibrationAttributes fixupVibrationAttributes(VibrationAttributes attrs) {
+ if (attrs == null) {
+ attrs = DEFAULT_ATTRIBUTES;
+ }
+ if (shouldBypassDnd(attrs)) {
+ if (!(hasPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+ || hasPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ || hasPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING))) {
+ final int flags = attrs.getFlags()
+ & ~VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
+ attrs = new VibrationAttributes.Builder(attrs).replaceFlags(flags).build();
+ }
+ }
+
+ return attrs;
+ }
+
private static long[] getLongIntArray(Resources r, int resid) {
int[] ar = r.getIntArray(resid);
if (ar == null) {
@@ -604,19 +622,7 @@
return;
}
- if (attrs == null) {
- attrs = DEFAULT_ATTRIBUTES;
- }
-
- if (shouldBypassDnd(attrs)) {
- if (!(hasPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
- || hasPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- || hasPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING))) {
- final int flags = attrs.getFlags()
- & ~VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
- attrs = new VibrationAttributes.Builder(attrs).replaceFlags(flags).build();
- }
- }
+ attrs = fixupVibrationAttributes(attrs);
// If our current vibration is longer than the new vibration and is the same amplitude,
// then just let the current one finish.
@@ -777,29 +783,8 @@
private void startVibrationLocked(final Vibration vib) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationLocked");
try {
- if (!isAllowedToVibrateLocked(vib)) {
- return;
- }
-
final int intensity = getCurrentIntensityLocked(vib);
- if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) {
- return;
- }
-
- if (vib.isRingtone() && !shouldVibrateForRingtone()) {
- if (DEBUG) {
- Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones");
- }
- return;
- }
-
- final int mode = getAppOpMode(vib);
- if (mode != AppOpsManager.MODE_ALLOWED) {
- if (mode == AppOpsManager.MODE_ERRORED) {
- // We might be getting calls from within system_server, so we don't actually
- // want to throw a SecurityException here.
- Slog.w(TAG, "Would be an error: vibrate from uid " + vib.uid);
- }
+ if (!shouldVibrate(vib, intensity)) {
return;
}
applyVibrationIntensityScalingLocked(vib, intensity);
@@ -958,6 +943,35 @@
return mode;
}
+ private boolean shouldVibrate(Vibration vib, int intensity) {
+ if (!isAllowedToVibrateLocked(vib)) {
+ return false;
+ }
+
+ if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) {
+ return false;
+ }
+
+ if (vib.isRingtone() && !shouldVibrateForRingtone()) {
+ if (DEBUG) {
+ Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones");
+ }
+ return false;
+ }
+
+ final int mode = getAppOpMode(vib);
+ if (mode != AppOpsManager.MODE_ALLOWED) {
+ if (mode == AppOpsManager.MODE_ERRORED) {
+ // We might be getting calls from within system_server, so we don't actually
+ // want to throw a SecurityException here.
+ Slog.w(TAG, "Would be an error: vibrate from uid " + vib.uid);
+ }
+ return false;
+ }
+
+ return true;
+ }
+
@GuardedBy("mLock")
private void reportFinishVibrationLocked() {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked");
@@ -1069,14 +1083,12 @@
mVibrator.getDefaultRingVibrationIntensity(), UserHandle.USER_CURRENT);
}
- private void updateAlwaysOnLocked(int id, VibrationEffect effect, VibrationAttributes attrs) {
- // TODO: Check DND and LowPower settings
- final Vibration vib = new Vibration(null, effect, attrs, 0, null, null);
+ private void updateAlwaysOnLocked(int id, Vibration vib) {
final int intensity = getCurrentIntensityLocked(vib);
- if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) {
+ if (!shouldVibrate(vib, intensity)) {
vibratorAlwaysOnDisable(id);
} else {
- final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect;
+ final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.effect;
final int strength = intensityToEffectStrength(intensity);
vibratorAlwaysOnEnable(id, prebaked.getId(), strength);
}
@@ -1085,8 +1097,8 @@
private void updateAlwaysOnLocked() {
for (int i = 0; i < mAlwaysOnEffects.size(); i++) {
int id = mAlwaysOnEffects.keyAt(i);
- Pair<VibrationEffect, VibrationAttributes> pair = mAlwaysOnEffects.valueAt(i);
- updateAlwaysOnLocked(id, pair.first, pair.second);
+ Vibration vib = mAlwaysOnEffects.valueAt(i);
+ updateAlwaysOnLocked(id, vib);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ea238f2..883e7c6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2555,7 +2555,7 @@
Process.setThreadGroupAndCpuset(BackgroundThread.get().getThreadId(),
Process.THREAD_GROUP_SYSTEM);
Process.setThreadGroupAndCpuset(
- mOomAdjuster.mAppCompact.mCompactionThread.getThreadId(),
+ mOomAdjuster.mCachedAppOptimizer.mCachedAppOptimizerThread.getThreadId(),
Process.THREAD_GROUP_SYSTEM);
} catch (Exception e) {
Slog.w(TAG, "Setting background thread cpuset failed");
@@ -5304,7 +5304,7 @@
String data, Bundle extras, boolean ordered,
boolean sticky, int sendingUser) {
synchronized (ActivityManagerService.this) {
- mOomAdjuster.mAppCompact.compactAllSystem();
+ mOomAdjuster.mCachedAppOptimizer.compactAllSystem();
requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);
}
}
@@ -9000,7 +9000,7 @@
final long timeSinceLastIdle = now - mLastIdleTime;
// Compact all non-zygote processes to freshen up the page cache.
- mOomAdjuster.mAppCompact.compactAllSystem();
+ mOomAdjuster.mCachedAppOptimizer.compactAllSystem();
final long lowRamSinceLastIdle = getLowRamTimeSinceIdle(now);
mLastIdleTime = now;
@@ -10021,7 +10021,7 @@
synchronized(this) {
mConstants.dump(pw);
- mOomAdjuster.dumpAppCompactorSettings(pw);
+ mOomAdjuster.dumpCachedAppOptimizerSettings(pw);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
@@ -10426,7 +10426,7 @@
} else if ("settings".equals(cmd)) {
synchronized (this) {
mConstants.dump(pw);
- mOomAdjuster.dumpAppCompactorSettings(pw);
+ mOomAdjuster.dumpCachedAppOptimizerSettings(pw);
}
} else if ("services".equals(cmd) || "s".equals(cmd)) {
if (dumpClient) {
diff --git a/services/core/java/com/android/server/am/AppCompactor.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
similarity index 97%
rename from services/core/java/com/android/server/am/AppCompactor.java
rename to services/core/java/com/android/server/am/CachedAppOptimizer.java
index b7e2065..3ca5ebc 100644
--- a/services/core/java/com/android/server/am/AppCompactor.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -51,7 +51,7 @@
import java.util.Random;
import java.util.Set;
-public final class AppCompactor {
+public final class CachedAppOptimizer {
// Flags stored in the DeviceConfig API.
@VisibleForTesting static final String KEY_USE_COMPACTION = "use_compaction";
@@ -122,7 +122,7 @@
* that will wipe out the cpuset assignment for system_server threads.
* Accordingly, this is in the AMS constructor.
*/
- final ServiceThread mCompactionThread;
+ final ServiceThread mCachedAppOptimizerThread;
private final ArrayList<ProcessRecord> mPendingCompactionProcesses =
new ArrayList<ProcessRecord>();
@@ -214,15 +214,15 @@
private int mPersistentCompactionCount;
private int mBfgsCompactionCount;
- public AppCompactor(ActivityManagerService am) {
+ public CachedAppOptimizer(ActivityManagerService am) {
mAm = am;
- mCompactionThread = new ServiceThread("CompactionThread",
+ mCachedAppOptimizerThread = new ServiceThread("CachedAppOptimizerThread",
THREAD_PRIORITY_FOREGROUND, true);
mProcStateThrottle = new HashSet<>();
}
@VisibleForTesting
- AppCompactor(ActivityManagerService am, PropertyChangedCallbackForTest callback) {
+ CachedAppOptimizer(ActivityManagerService am, PropertyChangedCallbackForTest callback) {
this(am);
mTestCallback = callback;
}
@@ -243,7 +243,7 @@
updateFullDeltaRssThrottle();
updateProcStateThrottle();
}
- Process.setThreadGroupAndCpuset(mCompactionThread.getThreadId(),
+ Process.setThreadGroupAndCpuset(mCachedAppOptimizerThread.getThreadId(),
Process.THREAD_GROUP_SYSTEM);
}
@@ -258,7 +258,7 @@
@GuardedBy("mAm")
void dump(PrintWriter pw) {
- pw.println("AppCompactor settings");
+ pw.println("CachedAppOptimizer settings");
synchronized (mPhenotypeFlagLock) {
pw.println(" " + KEY_USE_COMPACTION + "=" + mUseCompaction);
pw.println(" " + KEY_COMPACT_ACTION_1 + "=" + mCompactActionSome);
@@ -300,7 +300,7 @@
app.reqCompactAction = COMPACT_PROCESS_SOME;
mPendingCompactionProcesses.add(app);
mCompactionHandler.sendMessage(
- mCompactionHandler.obtainMessage(
+ mCompactionHandler.obtainMessage(
COMPACT_PROCESS_MSG, app.setAdj, app.setProcState));
}
@@ -309,7 +309,7 @@
app.reqCompactAction = COMPACT_PROCESS_FULL;
mPendingCompactionProcesses.add(app);
mCompactionHandler.sendMessage(
- mCompactionHandler.obtainMessage(
+ mCompactionHandler.obtainMessage(
COMPACT_PROCESS_MSG, app.setAdj, app.setProcState));
}
@@ -362,8 +362,8 @@
private void updateUseCompaction() {
mUseCompaction = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_USE_COMPACTION, DEFAULT_USE_COMPACTION);
- if (mUseCompaction && !mCompactionThread.isAlive()) {
- mCompactionThread.start();
+ if (mUseCompaction && !mCachedAppOptimizerThread.isAlive()) {
+ mCachedAppOptimizerThread.start();
mCompactionHandler = new MemCompactionHandler();
}
}
@@ -521,7 +521,7 @@
private final class MemCompactionHandler extends Handler {
private MemCompactionHandler() {
- super(mCompactionThread.getLooper());
+ super(mCachedAppOptimizerThread.getLooper());
}
@Override
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 0fc885a..f86d6a7 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -122,9 +122,9 @@
PowerManagerInternal mLocalPowerManager;
/**
- * Service for compacting background apps.
+ * Service for optimizing resource usage from background apps.
*/
- AppCompactor mAppCompact;
+ CachedAppOptimizer mCachedAppOptimizer;
ActivityManagerConstants mConstants;
@@ -197,7 +197,7 @@
mLocalPowerManager = LocalServices.getService(PowerManagerInternal.class);
mConstants = mService.mConstants;
- mAppCompact = new AppCompactor(mService);
+ mCachedAppOptimizer = new CachedAppOptimizer(mService);
mProcessGroupHandler = new Handler(adjusterThread.getLooper(), msg -> {
final int pid = msg.arg1;
@@ -224,7 +224,7 @@
}
void initSettings() {
- mAppCompact.init();
+ mCachedAppOptimizer.init();
}
/**
@@ -1978,7 +1978,7 @@
int changes = 0;
// don't compact during bootup
- if (mAppCompact.useCompaction() && mService.mBooted) {
+ if (mCachedAppOptimizer.useCompaction() && mService.mBooted) {
// Cached and prev/home compaction
if (app.curAdj != app.setAdj) {
// Perform a minor compaction when a perceptible app becomes the prev/home app
@@ -1987,26 +1987,26 @@
if (app.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ &&
(app.curAdj == ProcessList.PREVIOUS_APP_ADJ ||
app.curAdj == ProcessList.HOME_APP_ADJ)) {
- mAppCompact.compactAppSome(app);
+ mCachedAppOptimizer.compactAppSome(app);
} else if ((app.setAdj < ProcessList.CACHED_APP_MIN_ADJ
|| app.setAdj > ProcessList.CACHED_APP_MAX_ADJ)
&& app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ
&& app.curAdj <= ProcessList.CACHED_APP_MAX_ADJ) {
- mAppCompact.compactAppFull(app);
+ mCachedAppOptimizer.compactAppFull(app);
}
} else if (mService.mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE
&& app.setAdj < ProcessList.FOREGROUND_APP_ADJ
// Because these can fire independent of oom_adj/procstate changes, we need
// to throttle the actual dispatch of these requests in addition to the
// processing of the requests. As a result, there is throttling both here
- // and in AppCompactor.
- && mAppCompact.shouldCompactPersistent(app, now)) {
- mAppCompact.compactAppPersistent(app);
+ // and in CachedAppOptimizer.
+ && mCachedAppOptimizer.shouldCompactPersistent(app, now)) {
+ mCachedAppOptimizer.compactAppPersistent(app);
} else if (mService.mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE
&& app.getCurProcState()
== ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
- && mAppCompact.shouldCompactBFGS(app, now)) {
- mAppCompact.compactAppBfgs(app);
+ && mCachedAppOptimizer.shouldCompactBFGS(app, now)) {
+ mCachedAppOptimizer.compactAppBfgs(app);
}
}
@@ -2439,7 +2439,7 @@
}
@GuardedBy("mService")
- void dumpAppCompactorSettings(PrintWriter pw) {
- mAppCompact.dump(pw);
+ void dumpCachedAppOptimizerSettings(PrintWriter pw) {
+ mCachedAppOptimizer.dump(pw);
}
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index e11008c..b7f867d 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -647,11 +647,10 @@
// Get this after boot, and won't be changed until it's rebooted, as we don't
// want some apps enabled while some apps disabled
mAppDataIsolationEnabled =
- SystemProperties.getBoolean(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, false);
+ SystemProperties.getBoolean(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true);
mAppDataIsolationWhitelistedApps = new ArrayList<>(
SystemConfig.getInstance().getAppDataIsolationWhitelistedApps());
-
if (sKillHandler == null) {
sKillThread = new ServiceThread(TAG + ":kill",
THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
diff --git a/services/core/java/com/android/server/integrity/model/BitOutputStream.java b/services/core/java/com/android/server/integrity/model/BitOutputStream.java
index b8ea041..da778aa 100644
--- a/services/core/java/com/android/server/integrity/model/BitOutputStream.java
+++ b/services/core/java/com/android/server/integrity/model/BitOutputStream.java
@@ -16,17 +16,25 @@
package com.android.server.integrity.model;
-import java.util.BitSet;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Arrays;
/** A wrapper class for writing a stream of bits. */
public class BitOutputStream {
- private BitSet mBitSet;
- private int mIndex;
+ private static final int BUFFER_SIZE = 4 * 1024;
+ private static final int BYTE_BITS = 8;
- public BitOutputStream() {
- mBitSet = new BitSet();
- mIndex = 0;
+ private int mNextBitIndex;
+
+ private final OutputStream mOutputStream;
+ private final byte[] mBuffer;
+
+ public BitOutputStream(OutputStream outputStream) {
+ mBuffer = new byte[BUFFER_SIZE];
+ mNextBitIndex = 0;
+ mOutputStream = outputStream;
}
/**
@@ -35,15 +43,17 @@
* @param numOfBits The number of bits used to represent the value.
* @param value The value to convert to bits.
*/
- public void setNext(int numOfBits, int value) {
+ public void setNext(int numOfBits, int value) throws IOException {
if (numOfBits <= 0) {
return;
}
- int offset = 1 << (numOfBits - 1);
+
+ // optional: we can do some clever size checking to "OR" an entire segment of bits instead
+ // of setting bits one by one, but it is probably not worth it.
+ int nextBitMask = 1 << (numOfBits - 1);
while (numOfBits-- > 0) {
- mBitSet.set(mIndex, (value & offset) != 0);
- offset >>>= 1;
- mIndex++;
+ setNext((value & nextBitMask) != 0);
+ nextBitMask >>>= 1;
}
}
@@ -52,35 +62,43 @@
*
* @param value The value to set the bit to.
*/
- public void setNext(boolean value) {
- mBitSet.set(mIndex, value);
- mIndex++;
+ public void setNext(boolean value) throws IOException {
+ int byteToWrite = mNextBitIndex / BYTE_BITS;
+ if (byteToWrite == BUFFER_SIZE) {
+ mOutputStream.write(mBuffer);
+ reset();
+ byteToWrite = 0;
+ }
+ if (value) {
+ mBuffer[byteToWrite] |= 1 << (BYTE_BITS - 1 - (mNextBitIndex % BYTE_BITS));
+ }
+ mNextBitIndex++;
}
/** Set the next bit in the stream to true. */
- public void setNext() {
+ public void setNext() throws IOException {
setNext(/* value= */ true);
}
- /** Convert BitSet in big-endian to ByteArray in big-endian. */
- public byte[] toByteArray() {
- int bitSetSize = mBitSet.length();
- int numOfBytes = bitSetSize / 8;
- if (bitSetSize % 8 != 0) {
- numOfBytes++;
+ /**
+ * Flush the data written to the underlying {@link java.io.OutputStream}. Any unfinished bytes
+ * will be padded with 0.
+ */
+ public void flush() throws IOException {
+ int endByte = mNextBitIndex / BYTE_BITS;
+ if (mNextBitIndex % BYTE_BITS != 0) {
+ // If next bit is not the first bit of a byte, then mNextBitIndex / BYTE_BITS would be
+ // the byte that includes already written bits. We need to increment it so this byte
+ // gets written.
+ endByte++;
}
- byte[] bytes = new byte[numOfBytes];
- for (int i = 0; i < mBitSet.length(); i++) {
- if (mBitSet.get(i)) {
- bytes[i / 8] |= 1 << (7 - (i % 8));
- }
- }
- return bytes;
+ mOutputStream.write(mBuffer, 0, endByte);
+ reset();
}
- /** Clear the stream. */
- public void clear() {
- mBitSet.clear();
- mIndex = 0;
+ /** Reset this output stream to start state. */
+ private void reset() {
+ mNextBitIndex = 0;
+ Arrays.fill(mBuffer, (byte) 0);
}
}
diff --git a/services/core/java/com/android/server/integrity/model/ByteTrackedOutputStream.java b/services/core/java/com/android/server/integrity/model/ByteTrackedOutputStream.java
index f575599..0d6807a 100644
--- a/services/core/java/com/android/server/integrity/model/ByteTrackedOutputStream.java
+++ b/services/core/java/com/android/server/integrity/model/ByteTrackedOutputStream.java
@@ -23,31 +23,44 @@
* An output stream that tracks the total number written bytes since construction and allows
* querying this value any time during the execution.
*
- * This class is used for constructing the rule indexing.
+ * <p>This class is used for constructing the rule indexing.
*/
-public class ByteTrackedOutputStream {
+public class ByteTrackedOutputStream extends OutputStream {
- private static int sWrittenBytesCount;
- private static OutputStream sOutputStream;
+ private static final int INT_BYTES = 4;
+
+ private int mWrittenBytesCount;
+ private final OutputStream mOutputStream;
public ByteTrackedOutputStream(OutputStream outputStream) {
- sWrittenBytesCount = 0;
- sOutputStream = outputStream;
+ mWrittenBytesCount = 0;
+ mOutputStream = outputStream;
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ mWrittenBytesCount += INT_BYTES;
+ mOutputStream.write(b);
}
/**
- * Writes the given bytes into the output stream provided in constructor and updates the
- * total number of written bytes.
+ * Writes the given bytes into the output stream provided in constructor and updates the total
+ * number of written bytes.
*/
+ @Override
public void write(byte[] bytes) throws IOException {
- sWrittenBytesCount += bytes.length;
- sOutputStream.write(bytes);
+ mWrittenBytesCount += bytes.length;
+ mOutputStream.write(bytes);
}
- /**
- * Returns the total number of bytes written into the output stream at the requested time.
- */
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ mWrittenBytesCount += len;
+ mOutputStream.write(b, off, len);
+ }
+
+ /** Returns the total number of bytes written into the output stream at the requested time. */
public int getWrittenBytesCount() {
- return sWrittenBytesCount;
+ return mWrittenBytesCount;
}
}
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
index 8f53be7..6afadba 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
@@ -97,42 +97,38 @@
ruleFileByteTrackedOutputStream);
LinkedHashMap<String, Integer> unindexedRulesIndexes =
serializeRuleList(
- indexedRules.get(NOT_INDEXED),
- ruleFileByteTrackedOutputStream);
+ indexedRules.get(NOT_INDEXED), ruleFileByteTrackedOutputStream);
// Serialize their indexes.
- BitOutputStream indexingBitOutputStream = new BitOutputStream();
- serializeIndexGroup(packageNameIndexes, indexingBitOutputStream, /* isIndexed= */true);
- serializeIndexGroup(appCertificateIndexes, indexingBitOutputStream, /* isIndexed= */
- true);
- serializeIndexGroup(unindexedRulesIndexes, indexingBitOutputStream, /* isIndexed= */
- false);
- // TODO(b/147609625): This dummy bit is set for fixing the padding issue. Remove when
- // the issue is fixed and correct the tests that does this padding too.
- indexingBitOutputStream.setNext();
- indexingFileOutputStream.write(indexingBitOutputStream.toByteArray());
+ BitOutputStream indexingBitOutputStream = new BitOutputStream(indexingFileOutputStream);
+ serializeIndexGroup(packageNameIndexes, indexingBitOutputStream, /* isIndexed= */ true);
+ serializeIndexGroup(
+ appCertificateIndexes, indexingBitOutputStream, /* isIndexed= */ true);
+ serializeIndexGroup(
+ unindexedRulesIndexes, indexingBitOutputStream, /* isIndexed= */ false);
+ indexingBitOutputStream.flush();
} catch (Exception e) {
throw new RuleSerializeException(e.getMessage(), e);
}
}
- private void serializeRuleFileMetadata(Optional<Integer> formatVersion,
- ByteTrackedOutputStream outputStream)
+ private void serializeRuleFileMetadata(
+ Optional<Integer> formatVersion, ByteTrackedOutputStream outputStream)
throws IOException {
int formatVersionValue = formatVersion.orElse(DEFAULT_FORMAT_VERSION);
- BitOutputStream bitOutputStream = new BitOutputStream();
+ BitOutputStream bitOutputStream = new BitOutputStream(outputStream);
bitOutputStream.setNext(FORMAT_VERSION_BITS, formatVersionValue);
- outputStream.write(bitOutputStream.toByteArray());
+ bitOutputStream.flush();
}
private LinkedHashMap<String, Integer> serializeRuleList(
Map<String, List<Rule>> rulesMap, ByteTrackedOutputStream outputStream)
throws IOException {
- Preconditions.checkArgument(rulesMap != null,
- "serializeRuleList should never be called with null rule list.");
+ Preconditions.checkArgument(
+ rulesMap != null, "serializeRuleList should never be called with null rule list.");
- BitOutputStream bitOutputStream = new BitOutputStream();
+ BitOutputStream bitOutputStream = new BitOutputStream(outputStream);
LinkedHashMap<String, Integer> indexMapping = new LinkedHashMap();
indexMapping.put(START_INDEXING_KEY, outputStream.getWrittenBytesCount());
@@ -145,9 +141,8 @@
}
for (Rule rule : rulesMap.get(key)) {
- bitOutputStream.clear();
serializeRule(rule, bitOutputStream);
- outputStream.write(bitOutputStream.toByteArray());
+ bitOutputStream.flush();
indexTracker++;
}
}
@@ -156,7 +151,7 @@
return indexMapping;
}
- private void serializeRule(Rule rule, BitOutputStream bitOutputStream) {
+ private void serializeRule(Rule rule, BitOutputStream bitOutputStream) throws IOException {
if (rule == null) {
throw new IllegalArgumentException("Null rule can not be serialized");
}
@@ -171,7 +166,8 @@
bitOutputStream.setNext();
}
- private void serializeFormula(Formula formula, BitOutputStream bitOutputStream) {
+ private void serializeFormula(Formula formula, BitOutputStream bitOutputStream)
+ throws IOException {
if (formula instanceof AtomicFormula) {
serializeAtomicFormula((AtomicFormula) formula, bitOutputStream);
} else if (formula instanceof CompoundFormula) {
@@ -183,7 +179,7 @@
}
private void serializeCompoundFormula(
- CompoundFormula compoundFormula, BitOutputStream bitOutputStream) {
+ CompoundFormula compoundFormula, BitOutputStream bitOutputStream) throws IOException {
if (compoundFormula == null) {
throw new IllegalArgumentException("Null compound formula can not be serialized");
}
@@ -197,7 +193,7 @@
}
private void serializeAtomicFormula(
- AtomicFormula atomicFormula, BitOutputStream bitOutputStream) {
+ AtomicFormula atomicFormula, BitOutputStream bitOutputStream) throws IOException {
if (atomicFormula == null) {
throw new IllegalArgumentException("Null atomic formula can not be serialized");
}
@@ -231,11 +227,10 @@
private void serializeIndexGroup(
LinkedHashMap<String, Integer> indexes,
BitOutputStream bitOutputStream,
- boolean isIndexed) {
-
+ boolean isIndexed)
+ throws IOException {
// Output the starting location of this indexing group.
- serializeStringValue(
- START_INDEXING_KEY, /* isHashedValue= */false, bitOutputStream);
+ serializeStringValue(START_INDEXING_KEY, /* isHashedValue= */ false, bitOutputStream);
serializeIntValue(indexes.get(START_INDEXING_KEY), bitOutputStream);
// If the group is indexed, output the locations of the indexes.
@@ -243,8 +238,8 @@
for (Map.Entry<String, Integer> entry : indexes.entrySet()) {
if (!entry.getKey().equals(START_INDEXING_KEY)
&& !entry.getKey().equals(END_INDEXING_KEY)) {
- serializeStringValue(entry.getKey(), /* isHashedValue= */false,
- bitOutputStream);
+ serializeStringValue(
+ entry.getKey(), /* isHashedValue= */ false, bitOutputStream);
serializeIntValue(entry.getValue(), bitOutputStream);
}
}
@@ -256,7 +251,8 @@
}
private void serializeStringValue(
- String value, boolean isHashedValue, BitOutputStream bitOutputStream) {
+ String value, boolean isHashedValue, BitOutputStream bitOutputStream)
+ throws IOException {
if (value == null) {
throw new IllegalArgumentException("String value can not be null.");
}
@@ -269,11 +265,12 @@
}
}
- private void serializeIntValue(int value, BitOutputStream bitOutputStream) {
+ private void serializeIntValue(int value, BitOutputStream bitOutputStream) throws IOException {
bitOutputStream.setNext(/* numOfBits= */ 32, value);
}
- private void serializeBooleanValue(boolean value, BitOutputStream bitOutputStream) {
+ private void serializeBooleanValue(boolean value, BitOutputStream bitOutputStream)
+ throws IOException {
bitOutputStream.setNext(value);
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 0959133..b291a2e 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -2877,17 +2877,6 @@
}
@Override
- public void onTetheringChanged(String iface, boolean tethering) {
- // No need to enforce permission because setRestrictBackground() will do it.
- synchronized (mUidRulesFirstLock) {
- if (mRestrictBackground && tethering) {
- Log.d(TAG, "Tethering on (" + iface +"); disable Data Saver");
- setRestrictBackground(false);
- }
- }
- }
-
- @Override
public void setRestrictBackground(boolean restrictBackground) {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "setRestrictBackground");
try {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 159b4e4..13efdcd 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -190,6 +190,7 @@
import android.content.pm.ServiceInfo;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
import android.content.pm.VerifierDeviceIdentity;
@@ -20021,8 +20022,9 @@
String initiatingPackageName;
String originatingPackageName;
+ final InstallSource installSource;
synchronized (mLock) {
- final InstallSource installSource = getInstallSourceLocked(packageName, callingUid);
+ installSource = getInstallSourceLocked(packageName, callingUid);
if (installSource == null) {
return null;
}
@@ -20036,9 +20038,16 @@
}
if (installSource.isInitiatingPackageUninstalled) {
- // TODO(b/146555198) Allow the app itself to see the info
- // (at least for non-instant apps)
- initiatingPackageName = null;
+ // We can't check visibility in the usual way, since the initiating package is no
+ // longer present. So we apply simpler rules to whether to expose the info:
+ // 1. Instant apps can't see it.
+ // 2. Otherwise only the installed app itself can see it.
+ final boolean isInstantApp = getInstantAppPackageName(callingUid) != null;
+ if (!isInstantApp && isCallerSameApp(packageName, callingUid)) {
+ initiatingPackageName = installSource.initiatingPackageName;
+ } else {
+ initiatingPackageName = null;
+ }
} else {
// All installSource strings are interned, so == is ok here
if (installSource.initiatingPackageName == installSource.installerPackageName) {
@@ -20063,13 +20072,27 @@
}
}
+ // Remaining work can safely be done outside the lock. (Note that installSource is
+ // immutable so it's ok to carry on reading from it.)
+
if (originatingPackageName != null && mContext.checkCallingOrSelfPermission(
Manifest.permission.INSTALL_PACKAGES) != PackageManager.PERMISSION_GRANTED) {
originatingPackageName = null;
}
- return new InstallSourceInfo(initiatingPackageName, originatingPackageName,
- installerPackageName);
+ // If you can see the initiatingPackageName, and we have valid signing info for it,
+ // then we let you see that too.
+ final SigningInfo initiatingPackageSigningInfo;
+ final PackageSignatures signatures = installSource.initiatingPackageSignatures;
+ if (initiatingPackageName != null && signatures != null
+ && signatures.mSigningDetails != SigningDetails.UNKNOWN) {
+ initiatingPackageSigningInfo = new SigningInfo(signatures.mSigningDetails);
+ } else {
+ initiatingPackageSigningInfo = null;
+ }
+
+ return new InstallSourceInfo(initiatingPackageName, initiatingPackageSigningInfo,
+ originatingPackageName, installerPackageName);
}
@GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 2265d01..7888d1f 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -49,6 +49,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
+import android.os.ParcelableException;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -570,18 +571,17 @@
} else {
params.installFlags |= PackageManager.INSTALL_DISABLE_VERIFICATION;
}
- int apkSessionId = mPi.createSession(
- params, originalSession.getInstallerPackageName(),
- 0 /* UserHandle.SYSTEM */);
- PackageInstallerSession apkSession = mPi.getSession(apkSessionId);
-
try {
+ int apkSessionId = mPi.createSession(
+ params, originalSession.getInstallerPackageName(),
+ 0 /* UserHandle.SYSTEM */);
+ PackageInstallerSession apkSession = mPi.getSession(apkSessionId);
apkSession.open();
for (String apkFilePath : apkFilePaths) {
File apkFile = new File(apkFilePath);
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(apkFile,
ParcelFileDescriptor.MODE_READ_ONLY);
- long sizeBytes = pfd.getStatSize();
+ long sizeBytes = (pfd == null) ? -1 : pfd.getStatSize();
if (sizeBytes < 0) {
Slog.e(TAG, "Unable to get size of: " + apkFilePath);
throw new PackageManagerException(errorCode,
@@ -589,11 +589,11 @@
}
apkSession.write(apkFile.getName(), 0, sizeBytes, pfd);
}
- } catch (IOException e) {
+ return apkSession;
+ } catch (IOException | ParcelableException e) {
Slog.e(TAG, "Failure to install APK staged session " + originalSession.sessionId, e);
- throw new PackageManagerException(errorCode, "Failed to write APK session", e);
+ throw new PackageManagerException(errorCode, "Failed to create/write APK session", e);
}
- return apkSession;
}
/**
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 815f7b4..89030ed 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -199,13 +199,31 @@
);
/**
- * Special user restrictions that are applied globally when set by the profile owner of a
- * managed profile that was created during the device provisioning flow.
+ * Special user restrictions that profile owner of an organization-owned managed profile can
+ * set on the parent profile instance to apply them globally.
*/
private static final Set<String> PROFILE_OWNER_ORGANIZATION_OWNED_GLOBAL_RESTRICTIONS =
Sets.newArraySet(
UserManager.DISALLOW_CONFIG_DATE_TIME,
- UserManager.DISALLOW_CAMERA
+ UserManager.DISALLOW_CAMERA,
+ UserManager.DISALLOW_ADD_USER,
+ UserManager.DISALLOW_BLUETOOTH,
+ UserManager.DISALLOW_BLUETOOTH_SHARING,
+ UserManager.DISALLOW_CONFIG_BLUETOOTH,
+ UserManager.DISALLOW_CONFIG_CELL_BROADCASTS,
+ UserManager.DISALLOW_CONFIG_LOCATION,
+ UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
+ UserManager.DISALLOW_CONFIG_PRIVATE_DNS,
+ UserManager.DISALLOW_CONFIG_TETHERING,
+ UserManager.DISALLOW_CONFIG_WIFI,
+ UserManager.DISALLOW_CONTENT_CAPTURE,
+ UserManager.DISALLOW_CONTENT_SUGGESTIONS,
+ UserManager.DISALLOW_DATA_ROAMING,
+ UserManager.DISALLOW_DEBUGGING_FEATURES,
+ UserManager.DISALLOW_SAFE_BOOT,
+ UserManager.DISALLOW_SHARE_LOCATION,
+ UserManager.DISALLOW_SMS,
+ UserManager.DISALLOW_USB_FILE_TRANSFER
);
/**
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index 9f592b8..7d0072a 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -172,6 +172,13 @@
@Nullable public final String mInstallerPackageName;
/**
+ * This array holds all of the rollback tokens associated with package sessions included in
+ * this rollback.
+ */
+ @GuardedBy("mLock")
+ private final IntArray mTokens = new IntArray();
+
+ /**
* Constructs a new, empty Rollback instance.
*
* @param rollbackId the id of the rollback.
@@ -766,6 +773,26 @@
}
}
+ /**
+ * Adds a rollback token to be associated with this rollback. This may be used to
+ * identify which rollback should be removed in case {@link PackageManager} sends an
+ * {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK} intent.
+ */
+ void addToken(int token) {
+ synchronized (mLock) {
+ mTokens.add(token);
+ }
+ }
+
+ /**
+ * Returns true if this rollback is associated with the provided {@code token}.
+ */
+ boolean hasToken(int token) {
+ synchronized (mLock) {
+ return mTokens.indexOf(token) != -1;
+ }
+ }
+
static String rollbackStateToString(@RollbackState int state) {
switch (state) {
case Rollback.ROLLBACK_STATE_ENABLING: return "enabling";
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 8f8a5c4..eefcde6 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -235,12 +235,17 @@
Slog.v(TAG, "broadcast=ACTION_CANCEL_ENABLE_ROLLBACK token=" + token);
}
synchronized (mLock) {
- for (NewRollback rollback : mNewRollbacks) {
- if (rollback.hasToken(token)) {
- rollback.setCancelled();
- return;
+ NewRollback found = null;
+ for (NewRollback newRollback : mNewRollbacks) {
+ if (newRollback.rollback.hasToken(token)) {
+ found = newRollback;
+ break;
}
}
+ if (found != null) {
+ mNewRollbacks.remove(found);
+ found.rollback.delete(mAppDataRollbackHelper);
+ }
}
}
}
@@ -433,10 +438,14 @@
rollback.delete(mAppDataRollbackHelper);
}
}
- for (NewRollback newRollback : mNewRollbacks) {
+ Iterator<NewRollback> iter2 = mNewRollbacks.iterator();
+ while (iter2.hasNext()) {
+ NewRollback newRollback = iter2.next();
if (newRollback.rollback.includesPackage(packageName)) {
- newRollback.setCancelled();
+ iter2.remove();
+ newRollback.rollback.delete(mAppDataRollbackHelper);
}
+
}
}
}
@@ -798,7 +807,7 @@
mNewRollbacks.add(newRollback);
}
}
- newRollback.addToken(token);
+ newRollback.rollback.addToken(token);
return enableRollbackForPackageSession(newRollback.rollback, packageSession);
}
@@ -1220,12 +1229,6 @@
Slog.v(TAG, "completeEnableRollback id=" + rollback.info.getRollbackId());
}
- if (newRollback.isCancelled()) {
- Slog.e(TAG, "Rollback has been cancelled by PackageManager");
- rollback.delete(mAppDataRollbackHelper);
- return null;
- }
-
// We are checking if number of packages (excluding apk-in-apex) we enabled for rollback is
// equal to the number of sessions we are installing, to ensure we didn't skip enabling
// of any sessions. If we successfully enable an apex, then we can assume we enabled
@@ -1335,22 +1338,12 @@
public final Rollback rollback;
/**
- * This array holds all of the rollback tokens associated with package sessions included in
- * this rollback.
- */
- @GuardedBy("mNewRollbackLock")
- private final IntArray mTokens = new IntArray();
-
- /**
* Session ids for all packages in the install. For multi-package sessions, this is the list
* of child session ids. For normal sessions, this list is a single element with the normal
* session id.
*/
private final int[] mPackageSessionIds;
- @GuardedBy("mNewRollbackLock")
- private boolean mIsCancelled = false;
-
/**
* The number of sessions in the install which are notified with success by
* {@link PackageInstaller.SessionCallback#onFinished(int, boolean)}.
@@ -1367,52 +1360,6 @@
}
/**
- * Adds a rollback token to be associated with this NewRollback. This may be used to
- * identify which rollback should be cancelled in case {@link PackageManager} sends an
- * {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK} intent.
- */
- void addToken(int token) {
- synchronized (mNewRollbackLock) {
- mTokens.add(token);
- }
- }
-
- /**
- * Returns true if this NewRollback is associated with the provided {@code token}.
- */
- boolean hasToken(int token) {
- synchronized (mNewRollbackLock) {
- return mTokens.indexOf(token) != -1;
- }
- }
-
- /**
- * Returns true if this NewRollback has been cancelled.
- *
- * <p>Rollback could be invalidated and cancelled if RollbackManager receives
- * {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK} from {@link PackageManager}.
- *
- * <p>The main underlying assumption here is that if enabling the rollback times out, then
- * {@link PackageManager} will NOT send
- * {@link PackageInstaller.SessionCallback#onFinished(int, boolean)} before it broadcasts
- * {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK}.
- */
- boolean isCancelled() {
- synchronized (mNewRollbackLock) {
- return mIsCancelled;
- }
- }
-
- /**
- * Sets this NewRollback to be marked as cancelled.
- */
- void setCancelled() {
- synchronized (mNewRollbackLock) {
- mIsCancelled = true;
- }
- }
-
- /**
* Returns true if this NewRollback contains the provided {@code packageSessionId}.
*/
boolean containsSessionId(int packageSessionId) {
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index a0ef8cf..b9ef7b3 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -280,7 +280,6 @@
.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED,
WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN,
"");
- mContext.getSystemService(PowerManager.class).reboot("Rollback staged install");
} else if (sessionInfo.isStagedSessionFailed()
&& markStagedSessionHandled(rollbackId)) {
logEvent(moduleMetadataPackage,
@@ -291,6 +290,11 @@
}
}
}
+
+ // Wait for all pending staged sessions to get handled before rebooting.
+ if (isPendingStagedSessionsEmpty()) {
+ mContext.getSystemService(PowerManager.class).reboot("Rollback staged install");
+ }
}
/**
@@ -303,6 +307,16 @@
}
}
+ /**
+ * Returns {@code true} if all pending staged rollback sessions were marked as handled,
+ * {@code false} if there is any left.
+ */
+ private boolean isPendingStagedSessionsEmpty() {
+ synchronized (mPendingStagedRollbackIds) {
+ return mPendingStagedRollbackIds.isEmpty();
+ }
+ }
+
private void saveLastStagedRollbackId(int stagedRollbackId) {
try {
FileOutputStream fos = new FileOutputStream(mLastStagedRollbackIdFile);
@@ -414,6 +428,9 @@
reasonToLog, failedPackageToLog);
}
} else {
+ if (rollback.isStaged()) {
+ markStagedSessionHandled(rollback.getRollbackId());
+ }
logEvent(logPackage,
StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
reasonToLog, failedPackageToLog);
@@ -431,6 +448,16 @@
RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
List<RollbackInfo> rollbacks = rollbackManager.getAvailableRollbacks();
+ // Add all rollback ids to mPendingStagedRollbackIds, so that we do not reboot before all
+ // pending staged rollbacks are handled.
+ synchronized (mPendingStagedRollbackIds) {
+ for (RollbackInfo rollback : rollbacks) {
+ if (rollback.isStaged()) {
+ mPendingStagedRollbackIds.add(rollback.getRollbackId());
+ }
+ }
+ }
+
for (RollbackInfo rollback : rollbacks) {
VersionedPackage sample = rollback.getPackages().get(0).getVersionRolledBackFrom();
rollbackPackage(rollback, sample, PackageWatchdog.FAILURE_REASON_NATIVE_CRASH);
diff --git a/services/core/java/com/android/server/stats/StatsPullAtomService.java b/services/core/java/com/android/server/stats/StatsPullAtomService.java
index 45dee98..9290381 100644
--- a/services/core/java/com/android/server/stats/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/StatsPullAtomService.java
@@ -841,11 +841,22 @@
}
private void registerSystemUptime() {
- // No op.
+ int tagId = StatsLog.SYSTEM_UPTIME;
+ mStatsManager.registerPullAtomCallback(
+ tagId,
+ null, // use default PullAtomMetadata values
+ (atomTag, data) -> pullSystemUptime(atomTag, data),
+ Executors.newSingleThreadExecutor()
+ );
}
- private void pullSystemUptime() {
- // No op.
+ private int pullSystemUptime(int atomTag, List<StatsEvent> pulledData) {
+ StatsEvent e = StatsEvent.newBuilder()
+ .setAtomId(atomTag)
+ .writeLong(SystemClock.elapsedRealtime())
+ .build();
+ pulledData.add(e);
+ return StatsManager.PULL_SUCCESS;
}
private void registerRemainingBatteryCapacity() {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 320be2d..c9a702f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1633,12 +1633,7 @@
requestedVrComponent = (aInfo.requestedVrComponent == null) ?
null : ComponentName.unflattenFromString(aInfo.requestedVrComponent);
- lockTaskLaunchMode = aInfo.lockTaskLaunchMode;
- if (info.applicationInfo.isPrivilegedApp()
- && (lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_ALWAYS
- || lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_NEVER)) {
- lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT;
- }
+ lockTaskLaunchMode = getLockTaskLaunchMode(aInfo, options);
if (options != null) {
pendingOptions = options;
@@ -1646,15 +1641,27 @@
if (usageReport != null) {
appTimeTracker = new AppTimeTracker(usageReport);
}
- final boolean useLockTask = pendingOptions.getLockTaskMode();
- if (useLockTask && lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_DEFAULT) {
- lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
- }
// Gets launch display id from options. It returns INVALID_DISPLAY if not set.
mHandoverLaunchDisplayId = options.getLaunchDisplayId();
}
}
+ static int getLockTaskLaunchMode(ActivityInfo aInfo, @Nullable ActivityOptions options) {
+ int lockTaskLaunchMode = aInfo.lockTaskLaunchMode;
+ if (aInfo.applicationInfo.isPrivilegedApp()
+ && (lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_ALWAYS
+ || lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_NEVER)) {
+ lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT;
+ }
+ if (options != null) {
+ final boolean useLockTask = options.getLockTaskMode();
+ if (useLockTask && lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_DEFAULT) {
+ lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
+ }
+ }
+ return lockTaskLaunchMode;
+ }
+
@Override
ActivityRecord asActivityRecord() {
// I am an activity record!
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index df97caa..d61d29d 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -52,6 +52,7 @@
import android.os.UserManager;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.BlockedAppActivity;
import com.android.internal.app.HarmfulAppWarningActivity;
import com.android.internal.app.SuspendedAppActivity;
import com.android.internal.app.UnlaunchableAppActivity;
@@ -166,6 +167,9 @@
// no user action can undo this.
return true;
}
+ if (interceptLockTaskModeViolationPackageIfNeeded()) {
+ return true;
+ }
if (interceptHarmfulAppIfNeeded()) {
// If the app has a "harmful app" warning associated with it, we should ask to uninstall
// before issuing the work challenge.
@@ -262,6 +266,25 @@
return true;
}
+ private boolean interceptLockTaskModeViolationPackageIfNeeded() {
+ if (mAInfo == null || mAInfo.applicationInfo == null) {
+ return false;
+ }
+ LockTaskController controller = mService.getLockTaskController();
+ String packageName = mAInfo.applicationInfo.packageName;
+ int lockTaskLaunchMode = ActivityRecord.getLockTaskLaunchMode(mAInfo, mActivityOptions);
+ if (controller.isActivityAllowed(mUserId, packageName, lockTaskLaunchMode)) {
+ return false;
+ }
+ mIntent = BlockedAppActivity.createIntent(mUserId, mAInfo.applicationInfo.packageName);
+ mCallingPid = mRealCallingPid;
+ mCallingUid = mRealCallingUid;
+ mResolvedType = null;
+ mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0, mRealCallingUid);
+ mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
+ return true;
+ }
+
private boolean interceptWorkProfileChallengeIfNeeded() {
final Intent interceptingIntent = interceptWithConfirmCredentialsIfNeeded(mAInfo, mUserId);
if (interceptingIntent == null) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index ded603c..3092a08 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -28,7 +28,7 @@
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.Manifest.permission.STOP_APP_SWITCHES;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
-import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
+import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW;
import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
@@ -1436,7 +1436,7 @@
int handleIncomingUser(int callingPid, int callingUid, int userId, String name) {
return mAmInternal.handleIncomingUser(callingPid, callingUid, userId, false /* allowAll */,
- ALLOW_FULL_ONLY, name, null /* callerPackage */);
+ ALLOW_NON_FULL, name, null /* callerPackage */);
}
@Override
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index 02413bb..33b0453 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -23,6 +23,8 @@
import static android.content.Context.DEVICE_POLICY_SERVICE;
import static android.content.Context.STATUS_BAR_SERVICE;
import static android.content.Intent.ACTION_CALL_EMERGENCY;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
import static android.os.UserHandle.USER_ALL;
import static android.os.UserHandle.USER_CURRENT;
import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT;
@@ -339,6 +341,20 @@
& DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD) != 0;
}
+ boolean isActivityAllowed(int userId, String packageName, int lockTaskLaunchMode) {
+ if (mLockTaskModeState != LOCK_TASK_MODE_LOCKED) {
+ return true;
+ }
+ switch (lockTaskLaunchMode) {
+ case LOCK_TASK_LAUNCH_MODE_ALWAYS:
+ return true;
+ case LOCK_TASK_LAUNCH_MODE_NEVER:
+ return false;
+ default:
+ }
+ return isPackageWhitelisted(userId, packageName);
+ }
+
private boolean isEmergencyCallTask(Task task) {
final Intent intent = task.intent;
if (intent == null) {
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 03969b0..77d814e 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -51,7 +51,7 @@
"com_android_server_VibratorService.cpp",
"com_android_server_PersistentDataBlockService.cpp",
"com_android_server_GraphicsStatsService.cpp",
- "com_android_server_am_AppCompactor.cpp",
+ "com_android_server_am_CachedAppOptimizer.cpp",
"com_android_server_am_LowMemDetector.cpp",
"com_android_server_incremental_IncrementalManagerService.cpp",
"onload.cpp",
diff --git a/services/core/jni/com_android_server_am_AppCompactor.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
similarity index 89%
rename from services/core/jni/com_android_server_am_AppCompactor.cpp
rename to services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
index de6aa8b..6a6da0e 100644
--- a/services/core/jni/com_android_server_am_AppCompactor.cpp
+++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "AppCompactor"
+#define LOG_TAG "CachedAppOptimizer"
//#define LOG_NDEBUG 0
#include <dirent.h>
@@ -42,7 +42,7 @@
// or potentially some mainline modules. The only process that should definitely
// not be compacted is system_server, since compacting system_server around the
// time of BOOT_COMPLETE could result in perceptible issues.
-static void com_android_server_am_AppCompactor_compactSystem(JNIEnv *, jobject) {
+static void com_android_server_am_CachedAppOptimizer_compactSystem(JNIEnv *, jobject) {
std::unique_ptr<DIR, decltype(&closedir)> proc(opendir("/proc"), closedir);
struct dirent* current;
while ((current = readdir(proc.get()))) {
@@ -76,12 +76,12 @@
static const JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
- {"compactSystem", "()V", (void*)com_android_server_am_AppCompactor_compactSystem},
+ {"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem},
};
-int register_android_server_am_AppCompactor(JNIEnv* env)
+int register_android_server_am_CachedAppOptimizer(JNIEnv* env)
{
- return jniRegisterNativeMethods(env, "com/android/server/am/AppCompactor",
+ return jniRegisterNativeMethods(env, "com/android/server/am/CachedAppOptimizer",
sMethods, NELEM(sMethods));
}
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index c0a6e4e..19fa062 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -54,7 +54,7 @@
int register_android_server_net_NetworkStatsFactory(JNIEnv* env);
int register_android_server_net_NetworkStatsService(JNIEnv* env);
int register_android_server_security_VerityUtils(JNIEnv* env);
-int register_android_server_am_AppCompactor(JNIEnv* env);
+int register_android_server_am_CachedAppOptimizer(JNIEnv* env);
int register_android_server_am_LowMemDetector(JNIEnv* env);
int register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(
JNIEnv* env);
@@ -106,7 +106,7 @@
register_android_server_net_NetworkStatsFactory(env);
register_android_server_net_NetworkStatsService(env);
register_android_server_security_VerityUtils(env);
- register_android_server_am_AppCompactor(env);
+ register_android_server_am_CachedAppOptimizer(env);
register_android_server_am_LowMemDetector(env);
register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(
env);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index b6a8ca4..ea2385f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -213,6 +213,8 @@
"com.android.server.print.PrintManagerService";
private static final String COMPANION_DEVICE_MANAGER_SERVICE_CLASS =
"com.android.server.companion.CompanionDeviceManagerService";
+ private static final String STATS_COMPANION_APEX_PATH =
+ "/apex/com.android.os.statsd/javalib/service-statsd.jar";
private static final String STATS_COMPANION_LIFECYCLE_CLASS =
"com.android.server.stats.StatsCompanion$Lifecycle";
private static final String STATS_PULL_ATOM_SERVICE_CLASS =
@@ -1980,7 +1982,8 @@
// Statsd helper
t.traceBegin("StartStatsCompanion");
- mSystemServiceManager.startService(STATS_COMPANION_LIFECYCLE_CLASS);
+ mSystemServiceManager.startServiceFromJar(
+ STATS_COMPANION_LIFECYCLE_CLASS, STATS_COMPANION_APEX_PATH);
t.traceEnd();
// Statsd pulled atoms
diff --git a/services/net/Android.bp b/services/net/Android.bp
index 9c7cfc1..cf84bdf 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -23,7 +23,6 @@
name: "services-tethering-shared-srcs",
srcs: [
":framework-annotations",
- "java/android/net/util/NetdService.java",
"java/android/net/util/NetworkConstants.java",
],
visibility: ["//frameworks/base/packages/Tethering"],
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/AppCompactorTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AppCompactorTest.java
deleted file mode 100644
index 48e459f..0000000
--- a/services/tests/mockingservicestests/src/com/android/server/am/AppCompactorTest.java
+++ /dev/null
@@ -1,680 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.am;
-
-import static com.android.server.am.ActivityManagerService.Injector;
-import static com.android.server.am.AppCompactor.compactActionIntToString;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Context;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Process;
-import android.platform.test.annotations.Presubmit;
-import android.provider.DeviceConfig;
-import android.text.TextUtils;
-
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import com.android.server.ServiceThread;
-import com.android.server.appop.AppOpsService;
-import com.android.server.testables.TestableDeviceConfig;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
-
-import java.io.File;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Tests for {@link AppCompactor}.
- *
- * Build/Install/Run:
- * atest FrameworksMockingServicesTests:AppCompactorTest
- */
-@Presubmit
-@RunWith(MockitoJUnitRunner.class)
-public final class AppCompactorTest {
-
- private ServiceThread mThread;
-
- @Mock
- private AppOpsService mAppOpsService;
- private AppCompactor mCompactorUnderTest;
- private HandlerThread mHandlerThread;
- private Handler mHandler;
- private CountDownLatch mCountDown;
-
- @Rule
- public TestableDeviceConfig.TestableDeviceConfigRule
- mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule();
-
- @Before
- public void setUp() {
- mHandlerThread = new HandlerThread("");
- mHandlerThread.start();
- mHandler = new Handler(mHandlerThread.getLooper());
-
- mThread = new ServiceThread("TestServiceThread", Process.THREAD_PRIORITY_DEFAULT,
- true /* allowIo */);
- mThread.start();
-
- ActivityManagerService ams = new ActivityManagerService(
- new TestInjector(InstrumentationRegistry.getInstrumentation().getContext()),
- mThread);
- mCompactorUnderTest = new AppCompactor(ams,
- new AppCompactor.PropertyChangedCallbackForTest() {
- @Override
- public void onPropertyChanged() {
- if (mCountDown != null) {
- mCountDown.countDown();
- }
- }
- });
- }
-
- @After
- public void tearDown() {
- mHandlerThread.quit();
- mThread.quit();
- mCountDown = null;
- }
-
- @Test
- public void init_setsDefaults() {
- mCompactorUnderTest.init();
- assertThat(mCompactorUnderTest.useCompaction()).isEqualTo(
- AppCompactor.DEFAULT_USE_COMPACTION);
- assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo(
- compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_1));
- assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo(
- compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_2));
- assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_1);
- assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_2);
- assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_3);
- assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_4);
- assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo(
- AppCompactor.DEFAULT_STATSD_SAMPLE_RATE);
- assertThat(mCompactorUnderTest.mFullAnonRssThrottleKb).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
- assertThat(mCompactorUnderTest.mCompactThrottleBFGS).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_5);
- assertThat(mCompactorUnderTest.mCompactThrottlePersistent).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_6);
- assertThat(mCompactorUnderTest.mFullAnonRssThrottleKb).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
- assertThat(mCompactorUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB);
-
- Set<Integer> expected = new HashSet<>();
- for (String s : TextUtils.split(AppCompactor.DEFAULT_COMPACT_PROC_STATE_THROTTLE, ",")) {
- expected.add(Integer.parseInt(s));
- }
- assertThat(mCompactorUnderTest.mProcStateThrottle).containsExactlyElementsIn(expected);
- }
-
- @Test
- public void init_withDeviceConfigSetsParameters() {
- // When the DeviceConfig already has a flag value stored (note this test will need to
- // change if the default value changes from false).
- assertThat(AppCompactor.DEFAULT_USE_COMPACTION).isFalse();
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_USE_COMPACTION, "true", false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_ACTION_1,
- Integer.toString((AppCompactor.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1), false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_ACTION_2,
- Integer.toString((AppCompactor.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1), false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_THROTTLE_1,
- Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1), false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_THROTTLE_2,
- Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1), false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_THROTTLE_3,
- Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1), false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_THROTTLE_4,
- Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1), false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_THROTTLE_5,
- Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_5 + 1), false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_THROTTLE_6,
- Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_6 + 1), false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_STATSD_SAMPLE_RATE,
- Float.toString(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_FULL_RSS_THROTTLE_KB,
- Long.toString(AppCompactor.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1), false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB,
- Long.toString(AppCompactor.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + 1), false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_PROC_STATE_THROTTLE, "1,2,3", false);
-
- // Then calling init will read and set that flag.
- mCompactorUnderTest.init();
- assertThat(mCompactorUnderTest.useCompaction()).isTrue();
- assertThat(mCompactorUnderTest.mCompactionThread.isAlive()).isTrue();
-
- assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo(
- compactActionIntToString((AppCompactor.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1));
- assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo(
- compactActionIntToString((AppCompactor.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1));
- assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1);
- assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1);
- assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1);
- assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1);
- assertThat(mCompactorUnderTest.mCompactThrottleBFGS).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_5 + 1);
- assertThat(mCompactorUnderTest.mCompactThrottlePersistent).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_6 + 1);
- assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo(
- AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f);
- assertThat(mCompactorUnderTest.mCompactThrottleBFGS).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_5 + 1);
- assertThat(mCompactorUnderTest.mCompactThrottlePersistent).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_6 + 1);
- assertThat(mCompactorUnderTest.mFullAnonRssThrottleKb).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1);
- assertThat(mCompactorUnderTest.mProcStateThrottle).containsExactly(1, 2, 3);
- }
-
- @Test
- public void useCompaction_listensToDeviceConfigChanges() throws InterruptedException {
- assertThat(mCompactorUnderTest.useCompaction()).isEqualTo(
- AppCompactor.DEFAULT_USE_COMPACTION);
- // When we call init and change some the flag value...
- mCompactorUnderTest.init();
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_USE_COMPACTION, "true", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
- // Then that new flag value is updated in the implementation.
- assertThat(mCompactorUnderTest.useCompaction()).isTrue();
- assertThat(mCompactorUnderTest.mCompactionThread.isAlive()).isTrue();
-
- // And again, setting the flag the other way.
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_USE_COMPACTION, "false", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
- assertThat(mCompactorUnderTest.useCompaction()).isFalse();
- }
-
- @Test
- public void useCompaction_listensToDeviceConfigChangesBadValues() throws InterruptedException {
- assertThat(mCompactorUnderTest.useCompaction()).isEqualTo(
- AppCompactor.DEFAULT_USE_COMPACTION);
- mCompactorUnderTest.init();
-
- // When we push an invalid flag value...
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_USE_COMPACTION, "foobar", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
- // Then we set the default.
- assertThat(mCompactorUnderTest.useCompaction()).isEqualTo(
- AppCompactor.DEFAULT_USE_COMPACTION);
- }
-
- @Test
- public void compactAction_listensToDeviceConfigChanges() throws InterruptedException {
- mCompactorUnderTest.init();
-
- // When we override new values for the compaction action with reasonable values...
-
- // There are four possible values for compactAction[Some|Full].
- for (int i = 1; i < 5; i++) {
- mCountDown = new CountDownLatch(2);
- int expectedSome = (AppCompactor.DEFAULT_COMPACT_ACTION_1 + i) % 4 + 1;
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_ACTION_1, Integer.toString(expectedSome), false);
- int expectedFull = (AppCompactor.DEFAULT_COMPACT_ACTION_2 + i) % 4 + 1;
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_ACTION_2, Integer.toString(expectedFull), false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
- // Then the updates are reflected in the flags.
- assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo(
- compactActionIntToString(expectedSome));
- assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo(
- compactActionIntToString(expectedFull));
- }
- }
-
- @Test
- public void compactAction_listensToDeviceConfigChangesBadValues() throws InterruptedException {
- mCompactorUnderTest.init();
-
- // When we override new values for the compaction action with bad values ...
- mCountDown = new CountDownLatch(2);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_ACTION_1, "foo", false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_ACTION_2, "foo", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
- // Then the default values are reflected in the flag
- assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo(
- compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_1));
- assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo(
- compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_2));
-
- mCountDown = new CountDownLatch(2);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_ACTION_1, "", false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_ACTION_2, "", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
- assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo(
- compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_1));
- assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo(
- compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_2));
- }
-
- @Test
- public void compactThrottle_listensToDeviceConfigChanges() throws InterruptedException {
- mCompactorUnderTest.init();
-
- // When we override new reasonable throttle values after init...
- mCountDown = new CountDownLatch(6);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_THROTTLE_1,
- Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1), false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_THROTTLE_2,
- Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1), false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_THROTTLE_3,
- Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1), false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_THROTTLE_4,
- Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1), false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_THROTTLE_5,
- Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_5 + 1), false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_THROTTLE_6,
- Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_6 + 1), false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
- // Then those flags values are reflected in the compactor.
- assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1);
- assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1);
- assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1);
- assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1);
- assertThat(mCompactorUnderTest.mCompactThrottleBFGS).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_5 + 1);
- assertThat(mCompactorUnderTest.mCompactThrottlePersistent).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_6 + 1);
- }
-
- @Test
- public void compactThrottle_listensToDeviceConfigChangesBadValues()
- throws InterruptedException {
- mCompactorUnderTest.init();
-
- // When one of the throttles is overridden with a bad value...
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_THROTTLE_1, "foo", false);
- // Then all the throttles have the defaults set.
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
- assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_1);
- assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_2);
- assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_3);
- assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_4);
- assertThat(mCompactorUnderTest.mCompactThrottleBFGS).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_5);
- assertThat(mCompactorUnderTest.mCompactThrottlePersistent).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_6);
-
- // Repeat for each of the throttle keys.
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_THROTTLE_2, "foo", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
- assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_1);
- assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_2);
- assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_3);
- assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_4);
- assertThat(mCompactorUnderTest.mCompactThrottleBFGS).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_5);
- assertThat(mCompactorUnderTest.mCompactThrottlePersistent).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_6);
-
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_THROTTLE_3, "foo", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
- assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_1);
- assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_2);
- assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_3);
- assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_4);
- assertThat(mCompactorUnderTest.mCompactThrottleBFGS).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_5);
- assertThat(mCompactorUnderTest.mCompactThrottlePersistent).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_6);
-
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_THROTTLE_4, "foo", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
- assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_1);
- assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_2);
- assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_3);
- assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_4);
- assertThat(mCompactorUnderTest.mCompactThrottleBFGS).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_5);
- assertThat(mCompactorUnderTest.mCompactThrottlePersistent).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_6);
-
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_THROTTLE_5, "foo", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
- assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_1);
- assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_2);
- assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_3);
- assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_4);
- assertThat(mCompactorUnderTest.mCompactThrottleBFGS).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_5);
- assertThat(mCompactorUnderTest.mCompactThrottlePersistent).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_6);
-
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_THROTTLE_6, "foo", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
- assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_1);
- assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_2);
- assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_3);
- assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_4);
- assertThat(mCompactorUnderTest.mCompactThrottleBFGS).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_5);
- assertThat(mCompactorUnderTest.mCompactThrottlePersistent).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_THROTTLE_6);
- }
-
- @Test
- public void statsdSampleRate_listensToDeviceConfigChanges() throws InterruptedException {
- mCompactorUnderTest.init();
-
- // When we override mStatsdSampleRate with a reasonable value ...
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_STATSD_SAMPLE_RATE,
- Float.toString(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
- // Then that override is reflected in the compactor.
- assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo(
- AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f);
- }
-
- @Test
- public void statsdSampleRate_listensToDeviceConfigChangesBadValues()
- throws InterruptedException {
- mCompactorUnderTest.init();
-
- // When we override mStatsdSampleRate with an unreasonable value ...
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_STATSD_SAMPLE_RATE, "foo", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
- // Then that override is reflected in the compactor.
- assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo(
- AppCompactor.DEFAULT_STATSD_SAMPLE_RATE);
- }
-
- @Test
- public void statsdSampleRate_listensToDeviceConfigChangesOutOfRangeValues()
- throws InterruptedException {
- mCompactorUnderTest.init();
-
- // When we override mStatsdSampleRate with an value outside of [0..1]...
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_STATSD_SAMPLE_RATE,
- Float.toString(-1.0f), false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
- // Then the values is capped in the range.
- assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo(0.0f);
-
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_STATSD_SAMPLE_RATE,
- Float.toString(1.01f), false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
- // Then the values is capped in the range.
- assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo(1.0f);
- }
-
- @Test
- public void fullCompactionRssThrottleKb_listensToDeviceConfigChanges()
- throws InterruptedException {
- mCompactorUnderTest.init();
-
- // When we override mStatsdSampleRate with a reasonable value ...
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_FULL_RSS_THROTTLE_KB,
- Long.toString(AppCompactor.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1), false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
- // Then that override is reflected in the compactor.
- assertThat(mCompactorUnderTest.mFullAnonRssThrottleKb).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1);
- }
-
- @Test
- public void fullCompactionRssThrottleKb_listensToDeviceConfigChangesBadValues()
- throws InterruptedException {
- mCompactorUnderTest.init();
-
- // When we override mStatsdSampleRate with an unreasonable value ...
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_FULL_RSS_THROTTLE_KB, "foo", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
- // Then that override is reflected in the compactor.
- assertThat(mCompactorUnderTest.mFullAnonRssThrottleKb).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
-
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_FULL_RSS_THROTTLE_KB, "-100", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
- // Then that override is reflected in the compactor.
- assertThat(mCompactorUnderTest.mFullAnonRssThrottleKb).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
- }
-
- @Test
- public void fullCompactionDeltaRssThrottleKb_listensToDeviceConfigChanges()
- throws InterruptedException {
- mCompactorUnderTest.init();
-
- // When we override mStatsdSampleRate with a reasonable value ...
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB,
- Long.toString(AppCompactor.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + 1), false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
- // Then that override is reflected in the compactor.
- assertThat(mCompactorUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + 1);
- }
-
- @Test
- public void fullCompactionDeltaRssThrottleKb_listensToDeviceConfigChangesBadValues()
- throws InterruptedException {
- mCompactorUnderTest.init();
-
- // When we override mStatsdSampleRate with an unreasonable value ...
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB, "foo", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
- // Then that override is reflected in the compactor.
- assertThat(mCompactorUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB);
-
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB, "-100", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
- // Then that override is reflected in the compactor.
- assertThat(mCompactorUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
- AppCompactor.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB);
- }
-
- @Test
- public void procStateThrottle_listensToDeviceConfigChanges()
- throws InterruptedException {
- mCompactorUnderTest.init();
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_PROC_STATE_THROTTLE, "1,2,3", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
- assertThat(mCompactorUnderTest.mProcStateThrottle).containsExactly(1, 2, 3);
-
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_PROC_STATE_THROTTLE, "", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
- assertThat(mCompactorUnderTest.mProcStateThrottle).isEmpty();
- }
-
- @Test
- public void procStateThrottle_listensToDeviceConfigChangesBadValues()
- throws InterruptedException {
- mCompactorUnderTest.init();
-
- Set<Integer> expected = new HashSet<>();
- for (String s : TextUtils.split(AppCompactor.DEFAULT_COMPACT_PROC_STATE_THROTTLE, ",")) {
- expected.add(Integer.parseInt(s));
- }
-
- // Not numbers
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_PROC_STATE_THROTTLE, "foo", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
- assertThat(mCompactorUnderTest.mProcStateThrottle).containsExactlyElementsIn(expected);
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_PROC_STATE_THROTTLE, "1,foo", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
- assertThat(mCompactorUnderTest.mProcStateThrottle).containsExactlyElementsIn(expected);
-
- // Empty splits
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_PROC_STATE_THROTTLE, ",", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
- assertThat(mCompactorUnderTest.mProcStateThrottle).containsExactlyElementsIn(expected);
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_PROC_STATE_THROTTLE, ",,3", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
- assertThat(mCompactorUnderTest.mProcStateThrottle).containsExactlyElementsIn(expected);
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- AppCompactor.KEY_COMPACT_PROC_STATE_THROTTLE, "1,,3", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
- assertThat(mCompactorUnderTest.mProcStateThrottle).containsExactlyElementsIn(expected);
- }
-
- private class TestInjector extends Injector {
-
- TestInjector(Context context) {
- super(context);
- }
-
- @Override
- public AppOpsService getAppOpsService(File file, Handler handler) {
- return mAppOpsService;
- }
-
- @Override
- public Handler getUiHandler(ActivityManagerService service) {
- return mHandler;
- }
- }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
new file mode 100644
index 0000000..f037692
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
@@ -0,0 +1,692 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static com.android.server.am.ActivityManagerService.Injector;
+import static com.android.server.am.CachedAppOptimizer.compactActionIntToString;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Process;
+import android.platform.test.annotations.Presubmit;
+import android.provider.DeviceConfig;
+import android.text.TextUtils;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.ServiceThread;
+import com.android.server.appop.AppOpsService;
+import com.android.server.testables.TestableDeviceConfig;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests for {@link CachedAppOptimizer}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksMockingServicesTests:CachedAppOptimizerTest
+ */
+@Presubmit
+@RunWith(MockitoJUnitRunner.class)
+public final class CachedAppOptimizerTest {
+
+ private ServiceThread mThread;
+
+ @Mock
+ private AppOpsService mAppOpsService;
+ private CachedAppOptimizer mCachedAppOptimizerUnderTest;
+ private HandlerThread mHandlerThread;
+ private Handler mHandler;
+ private CountDownLatch mCountDown;
+
+ @Rule
+ public TestableDeviceConfig.TestableDeviceConfigRule
+ mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule();
+
+ @Before
+ public void setUp() {
+ mHandlerThread = new HandlerThread("");
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+
+ mThread = new ServiceThread("TestServiceThread", Process.THREAD_PRIORITY_DEFAULT,
+ true /* allowIo */);
+ mThread.start();
+
+ ActivityManagerService ams = new ActivityManagerService(
+ new TestInjector(InstrumentationRegistry.getInstrumentation().getContext()),
+ mThread);
+ mCachedAppOptimizerUnderTest = new CachedAppOptimizer(ams,
+ new CachedAppOptimizer.PropertyChangedCallbackForTest() {
+ @Override
+ public void onPropertyChanged() {
+ if (mCountDown != null) {
+ mCountDown.countDown();
+ }
+ }
+ });
+ }
+
+ @After
+ public void tearDown() {
+ mHandlerThread.quit();
+ mThread.quit();
+ mCountDown = null;
+ }
+
+ @Test
+ public void init_setsDefaults() {
+ mCachedAppOptimizerUnderTest.init();
+ assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo(
+ CachedAppOptimizer.DEFAULT_USE_COMPACTION);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo(
+ compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
+ assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo(
+ compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
+ assertThat(mCachedAppOptimizerUnderTest.mStatsdSampleRate).isEqualTo(
+ CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
+ assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6);
+ assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
+ assertThat(mCachedAppOptimizerUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB);
+
+ Set<Integer> expected = new HashSet<>();
+ for (String s : TextUtils.split(
+ CachedAppOptimizer.DEFAULT_COMPACT_PROC_STATE_THROTTLE, ",")) {
+ expected.add(Integer.parseInt(s));
+ }
+ assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle)
+ .containsExactlyElementsIn(expected);
+ }
+
+ @Test
+ public void init_withDeviceConfigSetsParameters() {
+ // When the DeviceConfig already has a flag value stored (note this test will need to
+ // change if the default value changes from false).
+ assertThat(CachedAppOptimizer.DEFAULT_USE_COMPACTION).isFalse();
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_USE_COMPACTION, "true", false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_ACTION_1,
+ Integer.toString((CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_ACTION_2,
+ Integer.toString((CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_THROTTLE_1,
+ Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1 + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_THROTTLE_2,
+ Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2 + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_THROTTLE_3,
+ Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3 + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_THROTTLE_4,
+ Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4 + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_THROTTLE_5,
+ Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5 + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_THROTTLE_6,
+ Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_STATSD_SAMPLE_RATE,
+ Float.toString(CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_FULL_RSS_THROTTLE_KB,
+ Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB,
+ Long.toString(
+ CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, "1,2,3", false);
+
+ // Then calling init will read and set that flag.
+ mCachedAppOptimizerUnderTest.init();
+ assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isTrue();
+ assertThat(mCachedAppOptimizerUnderTest.mCachedAppOptimizerThread.isAlive()).isTrue();
+
+ assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo(
+ compactActionIntToString(
+ (CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1));
+ assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo(
+ compactActionIntToString(
+ (CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1));
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1 + 1);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2 + 1);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3 + 1);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4 + 1);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5 + 1);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1);
+ assertThat(mCachedAppOptimizerUnderTest.mStatsdSampleRate).isEqualTo(
+ CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5 + 1);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1);
+ assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1);
+ assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle).containsExactly(1, 2, 3);
+ }
+
+ @Test
+ public void useCompaction_listensToDeviceConfigChanges() throws InterruptedException {
+ assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo(
+ CachedAppOptimizer.DEFAULT_USE_COMPACTION);
+ // When we call init and change some the flag value...
+ mCachedAppOptimizerUnderTest.init();
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_USE_COMPACTION, "true", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+ // Then that new flag value is updated in the implementation.
+ assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isTrue();
+ assertThat(mCachedAppOptimizerUnderTest.mCachedAppOptimizerThread.isAlive()).isTrue();
+
+ // And again, setting the flag the other way.
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_USE_COMPACTION, "false", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+ assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isFalse();
+ }
+
+ @Test
+ public void useCompaction_listensToDeviceConfigChangesBadValues() throws InterruptedException {
+ assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo(
+ CachedAppOptimizer.DEFAULT_USE_COMPACTION);
+ mCachedAppOptimizerUnderTest.init();
+
+ // When we push an invalid flag value...
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_USE_COMPACTION, "foobar", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+ // Then we set the default.
+ assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo(
+ CachedAppOptimizer.DEFAULT_USE_COMPACTION);
+ }
+
+ @Test
+ public void compactAction_listensToDeviceConfigChanges() throws InterruptedException {
+ mCachedAppOptimizerUnderTest.init();
+
+ // When we override new values for the compaction action with reasonable values...
+
+ // There are four possible values for compactAction[Some|Full].
+ for (int i = 1; i < 5; i++) {
+ mCountDown = new CountDownLatch(2);
+ int expectedSome = (CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1 + i) % 4 + 1;
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_ACTION_1, Integer.toString(expectedSome), false);
+ int expectedFull = (CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2 + i) % 4 + 1;
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_ACTION_2, Integer.toString(expectedFull), false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+ // Then the updates are reflected in the flags.
+ assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo(
+ compactActionIntToString(expectedSome));
+ assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo(
+ compactActionIntToString(expectedFull));
+ }
+ }
+
+ @Test
+ public void compactAction_listensToDeviceConfigChangesBadValues() throws InterruptedException {
+ mCachedAppOptimizerUnderTest.init();
+
+ // When we override new values for the compaction action with bad values ...
+ mCountDown = new CountDownLatch(2);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_ACTION_1, "foo", false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_ACTION_2, "foo", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+ // Then the default values are reflected in the flag
+ assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo(
+ compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
+ assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo(
+ compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
+
+ mCountDown = new CountDownLatch(2);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_ACTION_1, "", false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_ACTION_2, "", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+ assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo(
+ compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
+ assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo(
+ compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
+ }
+
+ @Test
+ public void compactThrottle_listensToDeviceConfigChanges() throws InterruptedException {
+ mCachedAppOptimizerUnderTest.init();
+
+ // When we override new reasonable throttle values after init...
+ mCountDown = new CountDownLatch(6);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_THROTTLE_1,
+ Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1 + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_THROTTLE_2,
+ Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2 + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_THROTTLE_3,
+ Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3 + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_THROTTLE_4,
+ Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4 + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_THROTTLE_5,
+ Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5 + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_THROTTLE_6,
+ Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1), false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+ // Then those flags values are reflected in the compactor.
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1 + 1);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2 + 1);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3 + 1);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4 + 1);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5 + 1);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1);
+ }
+
+ @Test
+ public void compactThrottle_listensToDeviceConfigChangesBadValues()
+ throws InterruptedException {
+ mCachedAppOptimizerUnderTest.init();
+
+ // When one of the throttles is overridden with a bad value...
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_THROTTLE_1, "foo", false);
+ // Then all the throttles have the defaults set.
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6);
+
+ // Repeat for each of the throttle keys.
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_THROTTLE_2, "foo", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6);
+
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_THROTTLE_3, "foo", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6);
+
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_THROTTLE_4, "foo", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6);
+
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_THROTTLE_5, "foo", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6);
+
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_THROTTLE_6, "foo", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6);
+ }
+
+ @Test
+ public void statsdSampleRate_listensToDeviceConfigChanges() throws InterruptedException {
+ mCachedAppOptimizerUnderTest.init();
+
+ // When we override mStatsdSampleRate with a reasonable value ...
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_STATSD_SAMPLE_RATE,
+ Float.toString(CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+ // Then that override is reflected in the compactor.
+ assertThat(mCachedAppOptimizerUnderTest.mStatsdSampleRate).isEqualTo(
+ CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f);
+ }
+
+ @Test
+ public void statsdSampleRate_listensToDeviceConfigChangesBadValues()
+ throws InterruptedException {
+ mCachedAppOptimizerUnderTest.init();
+
+ // When we override mStatsdSampleRate with an unreasonable value ...
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_STATSD_SAMPLE_RATE, "foo", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+ // Then that override is reflected in the compactor.
+ assertThat(mCachedAppOptimizerUnderTest.mStatsdSampleRate).isEqualTo(
+ CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
+ }
+
+ @Test
+ public void statsdSampleRate_listensToDeviceConfigChangesOutOfRangeValues()
+ throws InterruptedException {
+ mCachedAppOptimizerUnderTest.init();
+
+ // When we override mStatsdSampleRate with an value outside of [0..1]...
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_STATSD_SAMPLE_RATE,
+ Float.toString(-1.0f), false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+ // Then the values is capped in the range.
+ assertThat(mCachedAppOptimizerUnderTest.mStatsdSampleRate).isEqualTo(0.0f);
+
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_STATSD_SAMPLE_RATE,
+ Float.toString(1.01f), false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+ // Then the values is capped in the range.
+ assertThat(mCachedAppOptimizerUnderTest.mStatsdSampleRate).isEqualTo(1.0f);
+ }
+
+ @Test
+ public void fullCompactionRssThrottleKb_listensToDeviceConfigChanges()
+ throws InterruptedException {
+ mCachedAppOptimizerUnderTest.init();
+
+ // When we override mStatsdSampleRate with a reasonable value ...
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_FULL_RSS_THROTTLE_KB,
+ Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1), false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+ // Then that override is reflected in the compactor.
+ assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1);
+ }
+
+ @Test
+ public void fullCompactionRssThrottleKb_listensToDeviceConfigChangesBadValues()
+ throws InterruptedException {
+ mCachedAppOptimizerUnderTest.init();
+
+ // When we override mStatsdSampleRate with an unreasonable value ...
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_FULL_RSS_THROTTLE_KB, "foo", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+ // Then that override is reflected in the compactor.
+ assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
+
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_FULL_RSS_THROTTLE_KB, "-100", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+ // Then that override is reflected in the compactor.
+ assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
+ }
+
+ @Test
+ public void fullCompactionDeltaRssThrottleKb_listensToDeviceConfigChanges()
+ throws InterruptedException {
+ mCachedAppOptimizerUnderTest.init();
+
+ // When we override mStatsdSampleRate with a reasonable value ...
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB,
+ Long.toString(
+ CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + 1), false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+ // Then that override is reflected in the compactor.
+ assertThat(mCachedAppOptimizerUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + 1);
+ }
+
+ @Test
+ public void fullCompactionDeltaRssThrottleKb_listensToDeviceConfigChangesBadValues()
+ throws InterruptedException {
+ mCachedAppOptimizerUnderTest.init();
+
+ // When we override mStatsdSampleRate with an unreasonable value ...
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB, "foo", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+ // Then that override is reflected in the compactor.
+ assertThat(mCachedAppOptimizerUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB);
+
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB, "-100", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+ // Then that override is reflected in the compactor.
+ assertThat(mCachedAppOptimizerUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB);
+ }
+
+ @Test
+ public void procStateThrottle_listensToDeviceConfigChanges()
+ throws InterruptedException {
+ mCachedAppOptimizerUnderTest.init();
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, "1,2,3", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+ assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle).containsExactly(1, 2, 3);
+
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, "", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+ assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle).isEmpty();
+ }
+
+ @Test
+ public void procStateThrottle_listensToDeviceConfigChangesBadValues()
+ throws InterruptedException {
+ mCachedAppOptimizerUnderTest.init();
+
+ Set<Integer> expected = new HashSet<>();
+ for (String s : TextUtils.split(
+ CachedAppOptimizer.DEFAULT_COMPACT_PROC_STATE_THROTTLE, ",")) {
+ expected.add(Integer.parseInt(s));
+ }
+
+ // Not numbers
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, "foo", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+ assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle)
+ .containsExactlyElementsIn(expected);
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, "1,foo", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+ assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle)
+ .containsExactlyElementsIn(expected);
+
+ // Empty splits
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, ",", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+ assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle)
+ .containsExactlyElementsIn(expected);
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, ",,3", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+ assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle)
+ .containsExactlyElementsIn(expected);
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, "1,,3", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+ assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle)
+ .containsExactlyElementsIn(expected);
+ }
+
+ private class TestInjector extends Injector {
+
+ TestInjector(Context context) {
+ super(context);
+ }
+
+ @Override
+ public AppOpsService getAppOpsService(File file, Handler handler) {
+ return mAppOpsService;
+ }
+
+ @Override
+ public Handler getUiHandler(ActivityManagerService service) {
+ return mHandler;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index c223f13..e1e9b7e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -75,6 +75,7 @@
import android.os.IPowerManager;
import android.os.PowerManager;
import android.os.Process;
+import android.os.RemoteCallback;
import android.os.RemoteException;
import android.testing.DexmakerShareClassLoaderRule;
import android.view.Display;
@@ -693,6 +694,18 @@
assertThat(result, is(false));
}
+ @Test
+ public void takeScreenshot_returnNull() {
+ // no canTakeScreenshot, should return null.
+ when(mMockSecurityPolicy.canTakeScreenshotLocked(mServiceConnection)).thenReturn(false);
+ assertThat(mServiceConnection.takeScreenshot(Display.DEFAULT_DISPLAY), is(nullValue()));
+
+ // no checkAccessibilityAccess, should return null.
+ when(mMockSecurityPolicy.canTakeScreenshotLocked(mServiceConnection)).thenReturn(true);
+ when(mMockSecurityPolicy.checkAccessibilityAccess(mServiceConnection)).thenReturn(false);
+ assertThat(mServiceConnection.takeScreenshot(Display.DEFAULT_DISPLAY), is(nullValue()));
+ }
+
private void updateServiceInfo(AccessibilityServiceInfo serviceInfo, int eventType,
int feedbackType, int flags, String[] packageNames, int notificationTimeout) {
serviceInfo.eventTypes = eventType;
@@ -832,5 +845,8 @@
@Override
public void onFingerprintGesture(int gesture) {}
+
+ @Override
+ public void takeScreenshotWithCallback(int displayId, RemoteCallback callback) {}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
index 04ac7fe..1504097 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
@@ -346,6 +346,14 @@
}
@Test
+ public void canTakeScreenshot_hasCapability_returnTrue() {
+ when(mMockA11yServiceConnection.getCapabilities())
+ .thenReturn(AccessibilityServiceInfo.CAPABILITY_CAN_TAKE_SCREENSHOT);
+
+ assertTrue(mA11ySecurityPolicy.canTakeScreenshotLocked(mMockA11yServiceConnection));
+ }
+
+ @Test
public void resolveProfileParent_userIdIsCurrentUser_returnCurrentUser() {
final int currentUserId = 10;
final int userId = currentUserId;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
index 96d9c47..ac5169c 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
@@ -113,7 +113,6 @@
mUserState.mAccessibilityButtonTargets.add(COMPONENT_NAME.flattenToString());
mUserState.setTouchExplorationEnabledLocked(true);
mUserState.setDisplayMagnificationEnabledLocked(true);
- mUserState.setNavBarMagnificationEnabledLocked(true);
mUserState.setAutoclickEnabledLocked(true);
mUserState.setUserNonInteractiveUiTimeoutLocked(30);
mUserState.setUserInteractiveUiTimeoutLocked(30);
@@ -132,7 +131,6 @@
assertTrue(mUserState.mAccessibilityButtonTargets.isEmpty());
assertFalse(mUserState.isTouchExplorationEnabledLocked());
assertFalse(mUserState.isDisplayMagnificationEnabledLocked());
- assertFalse(mUserState.isNavBarMagnificationEnabledLocked());
assertFalse(mUserState.isAutoclickEnabledLocked());
assertEquals(0, mUserState.getUserNonInteractiveUiTimeoutLocked());
assertEquals(0, mUserState.getUserInteractiveUiTimeoutLocked());
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 45729e5..d670361 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -1968,6 +1968,29 @@
// TODO Make sure restrictions are written to the file.
}
+ private static final Set<String> PROFILE_OWNER_ORGANIZATION_OWNED_GLOBAL_RESTRICTIONS =
+ Sets.newSet(
+ UserManager.DISALLOW_CONFIG_DATE_TIME,
+ UserManager.DISALLOW_ADD_USER,
+ UserManager.DISALLOW_BLUETOOTH,
+ UserManager.DISALLOW_BLUETOOTH_SHARING,
+ UserManager.DISALLOW_CONFIG_BLUETOOTH,
+ UserManager.DISALLOW_CONFIG_CELL_BROADCASTS,
+ UserManager.DISALLOW_CONFIG_LOCATION,
+ UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
+ UserManager.DISALLOW_CONFIG_PRIVATE_DNS,
+ UserManager.DISALLOW_CONFIG_TETHERING,
+ UserManager.DISALLOW_CONFIG_WIFI,
+ UserManager.DISALLOW_CONTENT_CAPTURE,
+ UserManager.DISALLOW_CONTENT_SUGGESTIONS,
+ UserManager.DISALLOW_DATA_ROAMING,
+ UserManager.DISALLOW_DEBUGGING_FEATURES,
+ UserManager.DISALLOW_SAFE_BOOT,
+ UserManager.DISALLOW_SHARE_LOCATION,
+ UserManager.DISALLOW_SMS,
+ UserManager.DISALLOW_USB_FILE_TRANSFER
+ );
+
public void testSetUserRestriction_asPoOfOrgOwnedDevice() throws Exception {
final int MANAGED_PROFILE_USER_ID = DpmMockContext.CALLER_USER_HANDLE;
final int MANAGED_PROFILE_ADMIN_UID =
@@ -1980,15 +2003,9 @@
when(getServices().userManager.getProfileParent(MANAGED_PROFILE_USER_ID))
.thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
- parentDpm.addUserRestriction(admin1, UserManager.DISALLOW_CONFIG_DATE_TIME);
- verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
- eq(MANAGED_PROFILE_USER_ID),
- MockUtils.checkUserRestrictions(UserManager.DISALLOW_CONFIG_DATE_TIME),
- eq(UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE));
- reset(getServices().userManagerInternal);
-
- parentDpm.clearUserRestriction(admin1, UserManager.DISALLOW_CONFIG_DATE_TIME);
- reset(getServices().userManagerInternal);
+ for (String restriction : PROFILE_OWNER_ORGANIZATION_OWNED_GLOBAL_RESTRICTIONS) {
+ addAndRemoveUserRestrictionOnParentDpm(restriction);
+ }
parentDpm.setCameraDisabled(admin1, true);
verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
@@ -2005,6 +2022,20 @@
reset(getServices().userManagerInternal);
}
+ private void addAndRemoveUserRestrictionOnParentDpm(String restriction) {
+ parentDpm.addUserRestriction(admin1, restriction);
+ verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
+ eq(DpmMockContext.CALLER_USER_HANDLE),
+ MockUtils.checkUserRestrictions(restriction),
+ eq(UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE));
+ parentDpm.clearUserRestriction(admin1, restriction);
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions(),
+ parentDpm.getUserRestrictions(admin1)
+ );
+ reset(getServices().userManagerInternal);
+ }
+
public void testNoDefaultEnabledUserRestrictions() throws Exception {
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
mContext.callerPermissions.add(permission.MANAGE_USERS);
diff --git a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java
index 7a070ee..eda2b70 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java
@@ -188,7 +188,7 @@
}
@Test
- public void testEvaluateRules_ruleNotInDNF_ignoreAndAllow() {
+ public void testEvaluateRules_orRules() {
CompoundFormula compoundFormula =
new CompoundFormula(
CompoundFormula.OR,
@@ -206,11 +206,11 @@
IntegrityCheckResult result =
RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA);
- assertEquals(ALLOW, result.getEffect());
+ assertEquals(DENY, result.getEffect());
}
@Test
- public void testEvaluateRules_compoundFormulaWithNot_allow() {
+ public void testEvaluateRules_compoundFormulaWithNot_deny() {
CompoundFormula openSubFormula =
new CompoundFormula(
CompoundFormula.AND,
@@ -230,7 +230,7 @@
IntegrityCheckResult result =
RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA);
- assertEquals(ALLOW, result.getEffect());
+ assertEquals(DENY, result.getEffect());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/integrity/model/ByteTrackedOutputStreamTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/ByteTrackedOutputStreamTest.java
index c7cc343..57274bf 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/model/ByteTrackedOutputStreamTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/model/ByteTrackedOutputStreamTest.java
@@ -53,17 +53,17 @@
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ByteTrackedOutputStream byteTrackedOutputStream = new ByteTrackedOutputStream(outputStream);
- BitOutputStream bitOutputStream = new BitOutputStream();
+ BitOutputStream bitOutputStream = new BitOutputStream(byteTrackedOutputStream);
bitOutputStream.setNext(/* numOfBits= */5, /* value= */1);
- byteTrackedOutputStream.write(bitOutputStream.toByteArray());
+ bitOutputStream.flush();
// Even though we wrote 5 bits, this will complete to 1 byte.
assertThat(byteTrackedOutputStream.getWrittenBytesCount()).isEqualTo(1);
// Add a bit less than 2 bytes (10 bits).
- bitOutputStream.clear();
bitOutputStream.setNext(/* numOfBits= */10, /* value= */1);
- byteTrackedOutputStream.write(bitOutputStream.toByteArray());
+ bitOutputStream.flush();
+ assertThat(byteTrackedOutputStream.getWrittenBytesCount()).isEqualTo(3);
assertThat(outputStream.toByteArray().length).isEqualTo(3);
}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java
index 291cfee..742952e 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java
@@ -115,8 +115,7 @@
+ getKeyValueString(START_INDEXING_KEY, 500)
+ getKeyValueString(END_INDEXING_KEY, 900)
+ getKeyValueString(START_INDEXING_KEY, 900)
- + getKeyValueString(END_INDEXING_KEY, 945)
- + getBits(1, 1));
+ + getKeyValueString(END_INDEXING_KEY, 945));
ByteBuffer rule = ByteBuffer.allocate(stringBytes.length);
rule.put(stringBytes);
InputStream inputStream = new ByteArrayInputStream(rule.array());
@@ -153,8 +152,7 @@
+ getKeyValueString("888", 800)
+ getKeyValueString(END_INDEXING_KEY, 900)
+ getKeyValueString(START_INDEXING_KEY, 900)
- + getKeyValueString(END_INDEXING_KEY, 945)
- + getBits(1, 1));
+ + getKeyValueString(END_INDEXING_KEY, 945));
ByteBuffer rule = ByteBuffer.allocate(stringBytes.length);
rule.put(stringBytes);
return new ByteArrayInputStream(rule.array());
diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
index bc2aac0..8ee5f5f 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
@@ -139,10 +139,12 @@
SERIALIZED_START_INDEXING_KEY
+ getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32)
+ SERIALIZED_END_INDEXING_KEY
- + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */32);
+ + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32);
byte[] expectedIndexingBytes =
- getBytes(serializedIndexingBytes + serializedIndexingBytes
- + serializedIndexingBytes + getBits(1, 1));
+ getBytes(
+ serializedIndexingBytes
+ + serializedIndexingBytes
+ + serializedIndexingBytes);
expectedIndexingOutputStream.write(expectedIndexingBytes);
assertThat(indexingOutputStream.toByteArray())
.isEqualTo(expectedIndexingOutputStream.toByteArray());
@@ -200,11 +202,14 @@
SERIALIZED_START_INDEXING_KEY
+ getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32)
+ SERIALIZED_END_INDEXING_KEY
- + getBits(DEFAULT_FORMAT_VERSION_BYTES.length
- + getBytes(expectedBits).length, /* numOfBits= */ 32);
- expectedIndexingOutputStream.write(getBytes(
- expectedIndexingBitsForIndexed + expectedIndexingBitsForIndexed
- + expectedIndexingBitsForUnindexed + getBits(1, 1)));
+ + getBits(
+ DEFAULT_FORMAT_VERSION_BYTES.length + getBytes(expectedBits).length,
+ /* numOfBits= */ 32);
+ expectedIndexingOutputStream.write(
+ getBytes(
+ expectedIndexingBitsForIndexed
+ + expectedIndexingBitsForIndexed
+ + expectedIndexingBitsForUnindexed));
assertThat(indexingOutputStream.toByteArray())
.isEqualTo(expectedIndexingOutputStream.toByteArray());
@@ -513,16 +518,19 @@
// and 225 non-indexed rules..
List<Rule> ruleList = new ArrayList();
for (int count = 0; count < ruleCount; count++) {
- ruleList.add(getRuleWithPackageNameAndSampleInstallerName(
- String.format("%s%04d", packagePrefix, count)));
+ ruleList.add(
+ getRuleWithPackageNameAndSampleInstallerName(
+ String.format("%s%04d", packagePrefix, count)));
}
for (int count = 0; count < ruleCount; count++) {
- ruleList.add(getRuleWithAppCertificateAndSampleInstallerName(
- String.format("%s%04d", appCertificatePrefix, count)));
+ ruleList.add(
+ getRuleWithAppCertificateAndSampleInstallerName(
+ String.format("%s%04d", appCertificatePrefix, count)));
}
for (int count = 0; count < ruleCount; count++) {
- ruleList.add(getNonIndexedRuleWithInstallerName(
- String.format("%s%04d", installerNamePrefix, count)));
+ ruleList.add(
+ getNonIndexedRuleWithInstallerName(
+ String.format("%s%04d", installerNamePrefix, count)));
}
// Serialize the rules.
@@ -543,8 +551,7 @@
int totalBytesWritten = DEFAULT_FORMAT_VERSION_BYTES.length;
String expectedIndexingBytesForPackageNameIndexed =
- SERIALIZED_START_INDEXING_KEY
- + getBits(totalBytesWritten, /* numOfBits= */ 32);
+ SERIALIZED_START_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32);
for (int count = 0; count < ruleCount; count++) {
String packageName = String.format("%s%04d", packagePrefix, count);
if (count > 0 && count % INDEXING_BLOCK_SIZE == 0) {
@@ -556,18 +563,17 @@
}
byte[] bytesForPackage =
- getBytes(getSerializedCompoundRuleWithPackageNameAndSampleInstallerName(
- packageName));
+ getBytes(
+ getSerializedCompoundRuleWithPackageNameAndSampleInstallerName(
+ packageName));
expectedOrderedRuleOutputStream.write(bytesForPackage);
totalBytesWritten += bytesForPackage.length;
}
expectedIndexingBytesForPackageNameIndexed +=
- SERIALIZED_END_INDEXING_KEY
- + getBits(totalBytesWritten, /* numOfBits= */ 32);
+ SERIALIZED_END_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32);
String expectedIndexingBytesForAppCertificateIndexed =
- SERIALIZED_START_INDEXING_KEY
- + getBits(totalBytesWritten, /* numOfBits= */ 32);
+ SERIALIZED_START_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32);
for (int count = 0; count < ruleCount; count++) {
String appCertificate = String.format("%s%04d", appCertificatePrefix, count);
if (count > 0 && count % INDEXING_BLOCK_SIZE == 0) {
@@ -579,33 +585,32 @@
}
byte[] bytesForPackage =
- getBytes(getSerializedCompoundRuleWithCertificateNameAndSampleInstallerName(
- appCertificate));
+ getBytes(
+ getSerializedCompoundRuleWithCertificateNameAndSampleInstallerName(
+ appCertificate));
expectedOrderedRuleOutputStream.write(bytesForPackage);
totalBytesWritten += bytesForPackage.length;
}
expectedIndexingBytesForAppCertificateIndexed +=
- SERIALIZED_END_INDEXING_KEY
- + getBits(totalBytesWritten, /* numOfBits= */ 32);
+ SERIALIZED_END_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32);
String expectedIndexingBytesForUnindexed =
- SERIALIZED_START_INDEXING_KEY
- + getBits(totalBytesWritten, /* numOfBits= */ 32);
+ SERIALIZED_START_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32);
for (int count = 0; count < ruleCount; count++) {
byte[] bytesForPackage =
- getBytes(getSerializedCompoundRuleWithInstallerNameAndInstallerCert(
- String.format("%s%04d", installerNamePrefix, count)));
+ getBytes(
+ getSerializedCompoundRuleWithInstallerNameAndInstallerCert(
+ String.format("%s%04d", installerNamePrefix, count)));
expectedOrderedRuleOutputStream.write(bytesForPackage);
totalBytesWritten += bytesForPackage.length;
}
expectedIndexingBytesForUnindexed +=
- SERIALIZED_END_INDEXING_KEY
- + getBits(totalBytesWritten, /* numOfBits= */ 32);
+ SERIALIZED_END_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32);
expectedIndexingOutputStream.write(
- getBytes(expectedIndexingBytesForPackageNameIndexed
- + expectedIndexingBytesForAppCertificateIndexed
- + expectedIndexingBytesForUnindexed
- + getBits(1, 1)));
+ getBytes(
+ expectedIndexingBytesForPackageNameIndexed
+ + expectedIndexingBytesForAppCertificateIndexed
+ + expectedIndexingBytesForUnindexed));
assertThat(ruleOutputStream.toByteArray())
.isEqualTo(expectedOrderedRuleOutputStream.toByteArray());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index 399cf49..135d005 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
@@ -44,6 +45,7 @@
import androidx.test.filters.SmallTest;
+import com.android.internal.app.BlockedAppActivity;
import com.android.internal.app.HarmfulAppWarningActivity;
import com.android.internal.app.SuspendedAppActivity;
import com.android.internal.app.UnlaunchableAppActivity;
@@ -105,6 +107,8 @@
private PackageManagerService mPackageManager;
@Mock
private ActivityManagerInternal mAmInternal;
+ @Mock
+ private LockTaskController mLockTaskController;
private ActivityStartInterceptor mInterceptor;
private ActivityInfo mAInfo = new ActivityInfo();
@@ -145,6 +149,13 @@
when(mPackageManager.getHarmfulAppWarning(TEST_PACKAGE_NAME, TEST_USER_ID))
.thenReturn(null);
+ // Mock LockTaskController
+ mAInfo.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT;
+ when(mService.getLockTaskController()).thenReturn(mLockTaskController);
+ when(mLockTaskController.isActivityAllowed(
+ TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT))
+ .thenReturn(true);
+
// Initialise activity info
mAInfo.applicationInfo = new ApplicationInfo();
mAInfo.packageName = mAInfo.applicationInfo.packageName = TEST_PACKAGE_NAME;
@@ -196,6 +207,18 @@
}
@Test
+ public void testInterceptLockTaskModeViolationPackage() {
+ when(mLockTaskController.isActivityAllowed(
+ TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT))
+ .thenReturn(false);
+
+ assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
+
+ assertTrue(BlockedAppActivity.createIntent(TEST_USER_ID, TEST_PACKAGE_NAME)
+ .filterEquals(mInterceptor.mIntent));
+ }
+
+ @Test
public void testInterceptQuietProfile() {
// GIVEN that the user the activity is starting as is currently in quiet mode
when(mUserManager.isQuietModeEnabled(eq(UserHandle.of(TEST_USER_ID)))).thenReturn(true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
index 039ff60..75ec53d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
@@ -29,6 +29,9 @@
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
import static android.os.Process.SYSTEM_UID;
import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT;
@@ -693,6 +696,38 @@
assertTrue((StatusBarManager.DISABLE2_QUICK_SETTINGS & flags.second) != 0);
}
+ @Test
+ public void testIsActivityAllowed() {
+ // WHEN lock task mode is not enabled
+ assertTrue(mLockTaskController.isActivityAllowed(
+ TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT));
+
+ // WHEN lock task mode is enabled
+ Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+ mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
+
+
+ // package with LOCK_TASK_LAUNCH_MODE_ALWAYS should always be allowed
+ assertTrue(mLockTaskController.isActivityAllowed(
+ TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_ALWAYS));
+
+ // unwhitelisted package should not be allowed
+ assertFalse(mLockTaskController.isActivityAllowed(
+ TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT));
+
+ // update the whitelist
+ String[] whitelist = new String[] { TEST_PACKAGE_NAME };
+ mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist);
+
+ // whitelisted package should be allowed
+ assertTrue(mLockTaskController.isActivityAllowed(
+ TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT));
+
+ // package with LOCK_TASK_LAUNCH_MODE_NEVER should never be allowed
+ assertFalse(mLockTaskController.isActivityAllowed(
+ TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_NEVER));
+ }
+
private Task getTask(int lockTaskAuth) {
return getTask(TEST_PACKAGE_NAME, lockTaskAuth);
}
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
index 3877cc1..ce20311 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -23,12 +23,14 @@
import android.Manifest;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
import android.os.ParcelFileDescriptor;
+import android.os.storage.StorageManager;
import android.provider.DeviceConfig;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -185,12 +187,6 @@
*/
@Test
public void testNativeWatchdogTriggersRollback_Phase1() throws Exception {
- // When multiple staged sessions are installed on a device which doesn't support checkpoint,
- // only the 1st one will prevail. We have to check no other rollbacks available to ensure
- // TestApp.A is always the 1st and the only one to commit so rollback can work as intended.
- // If there are leftover rollbacks from previous tests, this assertion will fail.
- assertThat(RollbackUtils.getRollbackManager().getAvailableRollbacks()).isEmpty();
-
Uninstall.packages(TestApp.A);
Install.single(TestApp.A1).commit();
assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
@@ -220,6 +216,64 @@
TestApp.A)).isNotNull();
}
+ /**
+ * Stage install an apk with rollback that will be later triggered by unattributable crash.
+ */
+ @Test
+ public void testNativeWatchdogTriggersRollbackForAll_Phase1() throws Exception {
+ Uninstall.packages(TestApp.A);
+ Install.single(TestApp.A1).commit();
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+
+ Install.single(TestApp.A2).setEnableRollback().setStaged().commit();
+ }
+
+ /**
+ * Verify the rollback is available and then install another package with rollback.
+ */
+ @Test
+ public void testNativeWatchdogTriggersRollbackForAll_Phase2() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+ RollbackManager rm = RollbackUtils.getRollbackManager();
+ assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
+ TestApp.A)).isNotNull();
+
+ // Install another package with rollback
+ Uninstall.packages(TestApp.B);
+ Install.single(TestApp.B1).commit();
+ assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(1);
+
+ Install.single(TestApp.B2).setEnableRollback().setStaged().commit();
+ }
+
+ /**
+ * Verify the rollbacks are available.
+ */
+ @Test
+ public void testNativeWatchdogTriggersRollbackForAll_Phase3() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+ assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(2);
+ RollbackManager rm = RollbackUtils.getRollbackManager();
+ assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
+ TestApp.A)).isNotNull();
+ assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
+ TestApp.B)).isNotNull();
+ }
+
+ /**
+ * Verify the rollbacks are committed after crashing.
+ */
+ @Test
+ public void testNativeWatchdogTriggersRollbackForAll_Phase4() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+ assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(1);
+ RollbackManager rm = RollbackUtils.getRollbackManager();
+ assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
+ TestApp.A)).isNotNull();
+ assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
+ TestApp.B)).isNotNull();
+ }
+
@Test
public void testNetworkFailedRollback_Phase1() throws Exception {
// Remove available rollbacks and uninstall NetworkStack on /data/
@@ -438,6 +492,7 @@
RollbackManager rm = RollbackUtils.getRollbackManager();
rm.getAvailableRollbacks().stream().flatMap(info -> info.getPackages().stream())
.map(info -> info.getPackageName()).forEach(rm::expireRollbackForPackage);
+ assertThat(RollbackUtils.getRollbackManager().getAvailableRollbacks()).isEmpty();
}
private static final String APK_IN_APEX_TESTAPEX_NAME = "com.android.apex.apkrollback.test";
@@ -498,4 +553,11 @@
.executeShellCommand(cmd);
IoUtils.closeQuietly(pfd);
}
+
+ @Test
+ public void isCheckpointSupported() {
+ Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ StorageManager sm = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
+ assertThat(sm.isCheckpointSupported()).isTrue();
+ }
}
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index 6daa6bc..181e29a 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -17,6 +17,7 @@
package com.android.tests.rollback.host;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import static org.testng.Assert.assertThrows;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
@@ -62,6 +63,7 @@
"rm -f /system/apex/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex "
+ "/data/apex/active/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex");
getDevice().reboot();
+ runPhase("testCleanUp");
}
@After
@@ -95,7 +97,6 @@
@Test
public void testNativeWatchdogTriggersRollback() throws Exception {
- //Stage install ModuleMetadata package - this simulates a Mainline module update
runPhase("testNativeWatchdogTriggersRollback_Phase1");
// Reboot device to activate staged package
@@ -121,6 +122,40 @@
runPhase("testNativeWatchdogTriggersRollback_Phase3");
}
+ @Test
+ public void testNativeWatchdogTriggersRollbackForAll() throws Exception {
+ // This test requires committing multiple staged rollbacks
+ assumeTrue(isCheckpointSupported());
+
+ // Install a package with rollback enabled.
+ runPhase("testNativeWatchdogTriggersRollbackForAll_Phase1");
+ getDevice().reboot();
+
+ // Once previous staged install is applied, install another package
+ runPhase("testNativeWatchdogTriggersRollbackForAll_Phase2");
+ getDevice().reboot();
+
+ // Verify the new staged install has also been applied successfully.
+ runPhase("testNativeWatchdogTriggersRollbackForAll_Phase3");
+
+ // crash system_server enough times to trigger a rollback
+ crashProcess("system_server", NATIVE_CRASHES_THRESHOLD);
+
+ // Rollback should be committed automatically now.
+ // Give time for rollback to be committed. This could take a while,
+ // because we need all of the following to happen:
+ // 1. system_server comes back up and boot completes.
+ // 2. Rollback health observer detects updatable crashing signal.
+ // 3. Staged rollback session becomes ready.
+ // 4. Device actually reboots.
+ // So we give a generous timeout here.
+ assertTrue(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(5)));
+ getDevice().waitForDeviceAvailable();
+
+ // verify all available rollbacks have been committed
+ runPhase("testNativeWatchdogTriggersRollbackForAll_Phase4");
+ }
+
/**
* Tests failed network health check triggers watchdog staged rollbacks.
*/
@@ -244,4 +279,13 @@
// Find the NetworkStack path (can be NetworkStack.apk or NetworkStackNext.apk)
return getDevice().executeShellCommand("ls /system/priv-app/NetworkStack*/*.apk");
}
+
+ private boolean isCheckpointSupported() throws Exception {
+ try {
+ runPhase("isCheckpointSupported");
+ return true;
+ } catch (AssertionError ignore) {
+ return false;
+ }
+ }
}