Merge "Allow onSessionCommand return null"
diff --git a/api/current.txt b/api/current.txt
index 9f39f3a..491f677 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3941,7 +3941,8 @@
method public boolean isBackgroundRestricted();
method @Deprecated public boolean isInLockTaskMode();
method public boolean isLowRamDevice();
- method public static boolean isRunningInTestHarness();
+ method @Deprecated public static boolean isRunningInTestHarness();
+ method public static boolean isRunningInUserTestHarness();
method public static boolean isUserAMonkey();
method @RequiresPermission(android.Manifest.permission.KILL_BACKGROUND_PROCESSES) public void killBackgroundProcesses(String);
method @RequiresPermission(android.Manifest.permission.REORDER_TASKS) public void moveTaskToFront(int, int);
@@ -25912,6 +25913,23 @@
method @Nullable public android.media.Session2Command.Result onSessionCommand(@NonNull android.media.MediaSession2, @NonNull android.media.MediaSession2.ControllerInfo, @NonNull android.media.Session2Command, @Nullable android.os.Bundle);
}
+ public abstract class MediaSession2Service extends android.app.Service {
+ ctor public MediaSession2Service();
+ method public final void addSession(@NonNull android.media.MediaSession2);
+ method @NonNull public final java.util.List<android.media.MediaSession2> getSessions();
+ method @CallSuper @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
+ method @NonNull public abstract android.media.MediaSession2 onGetPrimarySession();
+ method @Nullable public abstract android.media.MediaSession2Service.MediaNotification onUpdateNotification(@NonNull android.media.MediaSession2);
+ method public final void removeSession(@NonNull android.media.MediaSession2);
+ field public static final String SERVICE_INTERFACE = "android.media.MediaSession2Service";
+ }
+
+ public static class MediaSession2Service.MediaNotification {
+ ctor public MediaSession2Service.MediaNotification(int, @NonNull android.app.Notification);
+ method @NonNull public android.app.Notification getNotification();
+ method public int getNotificationId();
+ }
+
public final class MediaSync {
ctor public MediaSync();
method @NonNull public android.view.Surface createInputSurface();
@@ -43596,6 +43614,7 @@
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telecom.PhoneAccountHandle> getSelfManagedPhoneAccounts();
method public android.telecom.PhoneAccountHandle getSimCallManager();
method public String getSystemDialerPackage();
+ method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telecom.PhoneAccountHandle getUserSelectedOutgoingPhoneAccount();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVoiceMailNumber(android.telecom.PhoneAccountHandle);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handleMmi(String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handleMmi(String, android.telecom.PhoneAccountHandle);
@@ -49674,6 +49693,7 @@
}
public class Surface implements android.os.Parcelable {
+ ctor public Surface(android.view.SurfaceControl);
ctor public Surface(android.graphics.SurfaceTexture);
method public int describeContents();
method public boolean isValid();
@@ -49696,6 +49716,38 @@
ctor public Surface.OutOfResourcesException(String);
}
+ public final class SurfaceControl implements android.os.Parcelable {
+ method public int describeContents();
+ method public boolean isValid();
+ method public void readFromParcel(android.os.Parcel);
+ method public void release();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.SurfaceControl> CREATOR;
+ }
+
+ public static class SurfaceControl.Builder {
+ ctor public SurfaceControl.Builder();
+ method public android.view.SurfaceControl build();
+ method public android.view.SurfaceControl.Builder setBufferSize(@IntRange(from=0) int, @IntRange(from=0) int);
+ method @NonNull public android.view.SurfaceControl.Builder setFormat(int);
+ method public android.view.SurfaceControl.Builder setName(String);
+ method @NonNull public android.view.SurfaceControl.Builder setOpaque(boolean);
+ method @NonNull public android.view.SurfaceControl.Builder setParent(@Nullable android.view.SurfaceControl);
+ }
+
+ public static class SurfaceControl.Transaction implements java.io.Closeable {
+ ctor public SurfaceControl.Transaction();
+ method public void apply();
+ method public void close();
+ method @NonNull public android.view.SurfaceControl.Transaction merge(@NonNull android.view.SurfaceControl.Transaction);
+ method @NonNull public android.view.SurfaceControl.Transaction reparent(@NonNull android.view.SurfaceControl, @Nullable android.view.SurfaceControl);
+ method @NonNull public android.view.SurfaceControl.Transaction setAlpha(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0, to=1.0) float);
+ method @NonNull public android.view.SurfaceControl.Transaction setBufferSize(@NonNull android.view.SurfaceControl, @IntRange(from=0) int, @IntRange(from=0) int);
+ method @NonNull public android.view.SurfaceControl.Transaction setGeometry(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Rect, @Nullable android.graphics.Rect, int);
+ method @NonNull public android.view.SurfaceControl.Transaction setLayer(@NonNull android.view.SurfaceControl, @IntRange(from=java.lang.Integer.MIN_VALUE, to=java.lang.Integer.MAX_VALUE) int);
+ method @NonNull public android.view.SurfaceControl.Transaction setVisibility(@NonNull android.view.SurfaceControl, boolean);
+ }
+
public interface SurfaceHolder {
method public void addCallback(android.view.SurfaceHolder.Callback);
method public android.view.Surface getSurface();
@@ -49740,6 +49792,7 @@
ctor public SurfaceView(android.content.Context, android.util.AttributeSet, int, int);
method public boolean gatherTransparentRegion(android.graphics.Region);
method public android.view.SurfaceHolder getHolder();
+ method public android.view.SurfaceControl getSurfaceControl();
method public void setSecure(boolean);
method public void setZOrderMediaOverlay(boolean);
method public void setZOrderOnTop(boolean);
diff --git a/api/system-current.txt b/api/system-current.txt
index 2088c14..6695b18 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6164,12 +6164,15 @@
method public abstract int onSwitchToSubscription(int, @Nullable String, boolean);
method public abstract int onUpdateSubscriptionNickname(int, String, String);
field public static final String ACTION_BIND_CARRIER_PROVISIONING_SERVICE = "android.service.euicc.action.BIND_CARRIER_PROVISIONING_SERVICE";
+ field public static final String ACTION_DELETE_SUBSCRIPTION_PRIVILEGED = "android.service.euicc.action.DELETE_SUBSCRIPTION_PRIVILEGED";
field public static final String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS = "android.service.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS";
field public static final String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION = "android.service.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION";
+ field public static final String ACTION_RENAME_SUBSCRIPTION_PRIVILEGED = "android.service.euicc.action.RENAME_SUBSCRIPTION_PRIVILEGED";
field @Deprecated public static final String ACTION_RESOLVE_CONFIRMATION_CODE = "android.service.euicc.action.RESOLVE_CONFIRMATION_CODE";
field public static final String ACTION_RESOLVE_DEACTIVATE_SIM = "android.service.euicc.action.RESOLVE_DEACTIVATE_SIM";
field public static final String ACTION_RESOLVE_NO_PRIVILEGES = "android.service.euicc.action.RESOLVE_NO_PRIVILEGES";
field public static final String ACTION_RESOLVE_RESOLVABLE_ERRORS = "android.service.euicc.action.RESOLVE_RESOLVABLE_ERRORS";
+ field public static final String ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED = "android.service.euicc.action.TOGGLE_SUBSCRIPTION_PRIVILEGED";
field public static final String CATEGORY_EUICC_UI = "android.service.euicc.category.EUICC_UI";
field public static final String EUICC_SERVICE_INTERFACE = "android.service.euicc.EuiccService";
field public static final String EXTRA_RESOLUTION_ALLOW_POLICY_RULES = "android.service.euicc.extra.RESOLUTION_ALLOW_POLICY_RULES";
@@ -6747,6 +6750,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall();
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging();
method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.WRITE_SECURE_SETTINGS}) public boolean setDefaultDialer(String);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUserSelectedOutgoingPhoneAccount(android.telecom.PhoneAccountHandle);
field public static final String EXTRA_CALL_BACK_INTENT = "android.telecom.extra.CALL_BACK_INTENT";
field public static final String EXTRA_CLEAR_MISSED_CALLS_INTENT = "android.telecom.extra.CLEAR_MISSED_CALLS_INTENT";
field public static final String EXTRA_CONNECTION_SERVICE = "android.telecom.extra.CONNECTION_SERVICE";
@@ -7912,9 +7916,12 @@
method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void getDefaultDownloadableSubscriptionList(android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void getDownloadableSubscriptionMetadata(android.telephony.euicc.DownloadableSubscription, android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public int getOtaStatus();
+ field public static final String ACTION_DELETE_SUBSCRIPTION_PRIVILEGED = "android.telephony.euicc.action.DELETE_SUBSCRIPTION_PRIVILEGED";
field @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public static final String ACTION_OTA_STATUS_CHANGED = "android.telephony.euicc.action.OTA_STATUS_CHANGED";
field public static final String ACTION_PROFILE_SELECTION = "android.telephony.euicc.action.PROFILE_SELECTION";
field public static final String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION = "android.telephony.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION";
+ field public static final String ACTION_RENAME_SUBSCRIPTION_PRIVILEGED = "android.telephony.euicc.action.RENAME_SUBSCRIPTION_PRIVILEGED";
+ field public static final String ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED = "android.telephony.euicc.action.TOGGLE_SUBSCRIPTION_PRIVILEGED";
field public static final int EUICC_ACTIVATION_TYPE_BACKUP = 2; // 0x2
field public static final int EUICC_ACTIVATION_TYPE_DEFAULT = 1; // 0x1
field public static final int EUICC_ACTIVATION_TYPE_TRANSFER = 3; // 0x3
@@ -7925,7 +7932,10 @@
field public static final int EUICC_OTA_SUCCEEDED = 3; // 0x3
field public static final String EXTRA_ACTIVATION_TYPE = "android.telephony.euicc.extra.ACTIVATION_TYPE";
field public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS";
+ field public static final String EXTRA_ENABLE_SUBSCRIPTION = "android.telephony.euicc.extra.ENABLE_SUBSCRIPTION";
field public static final String EXTRA_FORCE_PROVISION = "android.telephony.euicc.extra.FORCE_PROVISION";
+ field public static final String EXTRA_SUBSCRIPTION_ID = "android.telephony.euicc.extra.SUBSCRIPTION_ID";
+ field public static final String EXTRA_SUBSCRIPTION_NICKNAME = "android.telephony.euicc.extra.SUBSCRIPTION_NICKNAME";
}
@IntDef(prefix={"EUICC_OTA_"}, value={android.telephony.euicc.EuiccManager.EUICC_OTA_IN_PROGRESS, android.telephony.euicc.EuiccManager.EUICC_OTA_FAILED, android.telephony.euicc.EuiccManager.EUICC_OTA_SUCCEEDED, android.telephony.euicc.EuiccManager.EUICC_OTA_NOT_NEEDED, android.telephony.euicc.EuiccManager.EUICC_OTA_STATUS_UNAVAILABLE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface EuiccManager.OtaStatus {
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index 4174ad7..1b7fbfe 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -23,8 +23,10 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.telecom.Log;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
+import android.text.TextUtils;
import com.android.internal.os.BaseCommand;
import com.android.internal.telecom.ITelecomService;
@@ -45,6 +47,8 @@
private static final String COMMAND_SET_PHONE_ACCOUNT_ENABLED = "set-phone-account-enabled";
private static final String COMMAND_SET_PHONE_ACCOUNT_DISABLED = "set-phone-account-disabled";
private static final String COMMAND_REGISTER_PHONE_ACCOUNT = "register-phone-account";
+ private static final String COMMAND_SET_USER_SELECTED_OUTGOING_PHONE_ACCOUNT =
+ "set-user-selected-outgoing-phone-account";
private static final String COMMAND_REGISTER_SIM_PHONE_ACCOUNT = "register-sim-phone-account";
private static final String COMMAND_SET_TEST_CALL_REDIRECTION_APP = "set-test-call-redirection-app";
private static final String COMMAND_SET_TEST_CALL_SCREENING_APP = "set-test-call-screening-app";
@@ -70,6 +74,8 @@
+ "usage: telecom set-phone-account-enabled <COMPONENT> <ID> <USER_SN>\n"
+ "usage: telecom set-phone-account-disabled <COMPONENT> <ID> <USER_SN>\n"
+ "usage: telecom register-phone-account <COMPONENT> <ID> <USER_SN> <LABEL>\n"
+ + "usage: telecom set-user-selected-outgoing-phone-account <COMPONENT> <ID> "
+ + "<USER_SN>\n"
+ "usage: telecom set-test-call-redirection-app <PACKAGE>\n"
+ "usage: telecom set-test-call-screening-app <PACKAGE>\n"
+ "usage: telecom set-test-auto-mode-app <PACKAGE>\n"
@@ -104,16 +110,18 @@
mTelecomService = ITelecomService.Stub.asInterface(
ServiceManager.getService(Context.TELECOM_SERVICE));
if (mTelecomService == null) {
+ Log.w(this, "onRun: Can't access telecom manager.");
showError("Error: Could not access the Telecom Manager. Is the system running?");
return;
}
mUserManager = IUserManager.Stub
.asInterface(ServiceManager.getService(Context.USER_SERVICE));
if (mUserManager == null) {
+ Log.w(this, "onRun: Can't access user manager.");
showError("Error: Could not access the User Manager. Is the system running?");
return;
}
-
+ Log.i(this, "onRun: parsing command.");
String command = nextArgRequired();
switch (command) {
case COMMAND_SET_PHONE_ACCOUNT_ENABLED:
@@ -143,6 +151,9 @@
case COMMAND_REGISTER_SIM_PHONE_ACCOUNT:
runRegisterSimPhoneAccount();
break;
+ case COMMAND_SET_USER_SELECTED_OUTGOING_PHONE_ACCOUNT:
+ runSetUserSelectedOutgoingPhoneAccount();
+ break;
case COMMAND_UNREGISTER_PHONE_ACCOUNT:
runUnregisterPhoneAccount();
break;
@@ -159,6 +170,7 @@
runWaitOnHandler();
break;
default:
+ Log.w(this, "onRun: unknown command: %s", command);
throw new IllegalArgumentException ("unknown command '" + command + "'");
}
}
@@ -227,6 +239,13 @@
mTelecomService.setTestPhoneAcctSuggestionComponent(componentName);
}
+ private void runSetUserSelectedOutgoingPhoneAccount() throws RemoteException {
+ Log.i(this, "runSetUserSelectedOutgoingPhoneAccount");
+ final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
+ mTelecomService.setUserSelectedOutgoingPhoneAccount(handle);
+ System.out.println("Success - " + handle + " set as default outgoing account.");
+ }
+
private void runUnregisterPhoneAccount() throws RemoteException {
final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
mTelecomService.unregisterPhoneAccount(handle);
@@ -256,7 +275,10 @@
}
- private PhoneAccountHandle getPhoneAccountHandleFromArgs() throws RemoteException{
+ private PhoneAccountHandle getPhoneAccountHandleFromArgs() throws RemoteException {
+ if (TextUtils.isEmpty(mArgs.peekNextArg())) {
+ return null;
+ }
final ComponentName component = parseComponentName(nextArgRequired());
final String accountId = nextArgRequired();
final String userSnInStr = nextArgRequired();
@@ -265,6 +287,7 @@
final int userSn = Integer.parseInt(userSnInStr);
userHandle = UserHandle.of(mUserManager.getUserHandle(userSn));
} catch (NumberFormatException ex) {
+ Log.w(this, "getPhoneAccountHandleFromArgs - invalid user %s", userSnInStr);
throw new IllegalArgumentException ("Invalid user serial number " + userSnInStr);
}
return new PhoneAccountHandle(component, accountId, userHandle);
@@ -277,4 +300,4 @@
}
return cn;
}
-}
\ No newline at end of file
+}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index e0b8d78..1045b7a 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -3529,12 +3529,32 @@
/**
* Returns "true" if device is running in a test harness.
+ *
+ * @deprecated this method is false for all user builds. Users looking to check if their device
+ * is running in a device farm should see {@link #isRunningInUserTestHarness()}.
*/
+ @Deprecated
public static boolean isRunningInTestHarness() {
return SystemProperties.getBoolean("ro.test_harness", false);
}
/**
+ * Returns "true" if the device is running in Test Harness Mode.
+ *
+ * <p>Test Harness Mode is a feature that allows devices to run without human interaction in a
+ * device farm/testing harness (such as Firebase Test Lab). You should check this method if you
+ * want your app to behave differently when running in a test harness to skip setup screens that
+ * would impede UI testing. e.g. a keyboard application that has a full screen setup page for
+ * the first time it is launched.
+ *
+ * <p>Note that you should <em>not</em> use this to determine whether or not your app is running
+ * an instrumentation test, as it is not set for a standard device running a test.
+ */
+ public static boolean isRunningInUserTestHarness() {
+ return SystemProperties.getBoolean("persist.sys.test_harness", false);
+ }
+
+ /**
* Unsupported compiled sdk warning should always be shown for the intput activity
* even in cases where the system would normally not show the warning. E.g. when running in a
* test harness.
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index ab8f234..4d3711a 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -328,7 +328,7 @@
}
} else {
mTmpTransaction.reparent(mRootSurfaceControl,
- mSurfaceView.getSurfaceControl().getHandle()).apply();
+ mSurfaceView.getSurfaceControl()).apply();
}
if (mVirtualDisplay != null) {
@@ -390,7 +390,7 @@
.build();
try {
- wm.reparentDisplayContent(displayId, mRootSurfaceControl.getHandle());
+ wm.reparentDisplayContent(displayId, mRootSurfaceControl);
wm.dontOverrideDisplayInfo(displayId);
if (mSingleTaskInstance) {
mActivityTaskManager.setDisplayToSingleTaskInstance(displayId);
diff --git a/core/java/android/app/VrManager.java b/core/java/android/app/VrManager.java
index 5b87de4..5f1a94c 100644
--- a/core/java/android/app/VrManager.java
+++ b/core/java/android/app/VrManager.java
@@ -215,19 +215,12 @@
}
/**
- * Start VR Input method for the packageName in {@link ComponentName}.
- * This method notifies InputMethodManagerService to use VR IME instead of
- * regular phone IME.
- * @param componentName ComponentName of a VR InputMethod that should be set as selected
- * input by InputMethodManagerService.
+ * This method is not implemented.
+ *
+ * @param componentName not used
*/
@RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS)
public void setVrInputMethod(ComponentName componentName) {
- try {
- mService.setVrInputMethod(componentName);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
}
/**
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index ca39051..51c3c4c 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -72,6 +72,7 @@
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Comparator;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -831,6 +832,16 @@
return false;
}
+ /** {@hide} */
+ public static boolean contains(Collection<File> dirs, File file) {
+ for (File dir : dirs) {
+ if (contains(dir, file)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Test if a file lives under the given directory, either as a direct child
* or a distant grandchild.
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index df1a713..714a061 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -35,6 +35,7 @@
import java.io.CharArrayWriter;
import java.io.File;
+import java.util.Locale;
/**
* Information about a shared/external storage volume for a specific user.
@@ -263,6 +264,11 @@
return mFsUuid;
}
+ /** {@hide} */
+ public @Nullable String getNormalizedUuid() {
+ return mFsUuid != null ? mFsUuid.toLowerCase(Locale.US) : null;
+ }
+
/**
* Parse and return volume UUID as FAT volume ID, or return -1 if unable to
* parse or UUID is unknown.
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index 8c3aa17..5d310e1 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -42,6 +42,7 @@
import java.io.CharArrayWriter;
import java.io.File;
import java.util.Comparator;
+import java.util.Locale;
import java.util.Objects;
/**
@@ -254,6 +255,10 @@
return fsUuid;
}
+ public @Nullable String getNormalizedFsUuid() {
+ return fsUuid != null ? fsUuid.toLowerCase(Locale.US) : null;
+ }
+
@UnsupportedAppUsage
public int getMountUserId() {
return mountUserId;
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 3a49986..487198b 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -70,6 +70,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -1224,7 +1226,7 @@
if (sv.isPrimary()) {
return VOLUME_EXTERNAL;
} else {
- return checkArgumentVolumeName(sv.getUuid());
+ return checkArgumentVolumeName(sv.getNormalizedUuid());
}
}
throw new IllegalStateException("Unknown volume at " + path);
@@ -2919,7 +2921,7 @@
if (vi.isPrimary()) {
volumeNames.add(VOLUME_EXTERNAL);
} else {
- volumeNames.add(vi.getFsUuid());
+ volumeNames.add(vi.getNormalizedFsUuid());
}
}
}
@@ -2953,8 +2955,7 @@
// When not one of the well-known values above, it must be a hex UUID
for (int i = 0; i < volumeName.length(); i++) {
final char c = volumeName.charAt(i);
- if (('a' <= c && c <= 'f') || ('A' <= c && c <= 'F')
- || ('0' <= c && c <= '9') || (c == '-')) {
+ if (('a' <= c && c <= 'f') || ('0' <= c && c <= '9') || (c == '-')) {
continue;
} else {
throw new IllegalArgumentException("Invalid volume name: " + volumeName);
@@ -2963,23 +2964,26 @@
return volumeName;
}
- /** {@hide} */
+ /**
+ * Return path where the given volume is mounted. Not valid for
+ * {@link #VOLUME_INTERNAL}.
+ *
+ * @hide
+ */
public static @NonNull File getVolumePath(@NonNull String volumeName)
throws FileNotFoundException {
if (TextUtils.isEmpty(volumeName)) {
throw new IllegalArgumentException();
}
- if (VOLUME_INTERNAL.equals(volumeName)) {
- return Environment.getDataDirectory();
- } else if (VOLUME_EXTERNAL.equals(volumeName)) {
+ if (VOLUME_EXTERNAL.equals(volumeName)) {
return Environment.getExternalStorageDirectory();
}
final StorageManager sm = AppGlobals.getInitialApplication()
.getSystemService(StorageManager.class);
for (VolumeInfo vi : sm.getVolumes()) {
- if (Objects.equals(vi.getFsUuid(), volumeName)) {
+ if (Objects.equals(vi.getNormalizedFsUuid(), volumeName)) {
final File path = vi.getPathForUser(UserHandle.myUserId());
if (path != null) {
return path;
@@ -2992,6 +2996,33 @@
}
/**
+ * Return paths that should be scanned for the given volume.
+ *
+ * @hide
+ */
+ public static @NonNull Collection<File> getVolumeScanPaths(@NonNull String volumeName)
+ throws FileNotFoundException {
+ if (TextUtils.isEmpty(volumeName)) {
+ throw new IllegalArgumentException();
+ }
+
+ final ArrayList<File> res = new ArrayList<>();
+ if (VOLUME_INTERNAL.equals(volumeName)) {
+ res.add(new File(Environment.getRootDirectory(), "media"));
+ res.add(new File(Environment.getOemDirectory(), "media"));
+ res.add(new File(Environment.getProductDirectory(), "media"));
+ } else {
+ res.add(getVolumePath(volumeName));
+ final UserManager um = AppGlobals.getInitialApplication()
+ .getSystemService(UserManager.class);
+ if (VOLUME_EXTERNAL.equals(volumeName) && um.isDemoUser()) {
+ res.add(Environment.getDataPreloadsMediaDirectory());
+ }
+ }
+ return res;
+ }
+
+ /**
* Uri for querying the state of the media scanner.
*/
public static Uri getMediaScannerUri() {
diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java
index 4dc10cd..ffb524d 100644
--- a/core/java/android/service/euicc/EuiccService.java
+++ b/core/java/android/service/euicc/EuiccService.java
@@ -103,10 +103,23 @@
*/
public static final String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS =
"android.service.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS";
+
/** @see android.telephony.euicc.EuiccManager#ACTION_PROVISION_EMBEDDED_SUBSCRIPTION */
public static final String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION =
"android.service.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION";
+ /** @see android.telephony.euicc.EuiccManager#ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED */
+ public static final String ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED =
+ "android.service.euicc.action.TOGGLE_SUBSCRIPTION_PRIVILEGED";
+
+ /** @see android.telephony.euicc.EuiccManager#ACTION_DELETE_SUBSCRIPTION_PRIVILEGED */
+ public static final String ACTION_DELETE_SUBSCRIPTION_PRIVILEGED =
+ "android.service.euicc.action.DELETE_SUBSCRIPTION_PRIVILEGED";
+
+ /** @see android.telephony.euicc.EuiccManager#ACTION_RENAME_SUBSCRIPTION_PRIVILEGED */
+ public static final String ACTION_RENAME_SUBSCRIPTION_PRIVILEGED =
+ "android.service.euicc.action.RENAME_SUBSCRIPTION_PRIVILEGED";
+
// LUI resolution actions. These are called by the platform to resolve errors in situations that
// require user interaction.
// TODO(b/33075886): Define extras for any input parameters to these dialogs once they are
diff --git a/core/java/android/service/vr/IVrManager.aidl b/core/java/android/service/vr/IVrManager.aidl
index f7acfc5..b0269e3 100644
--- a/core/java/android/service/vr/IVrManager.aidl
+++ b/core/java/android/service/vr/IVrManager.aidl
@@ -110,13 +110,5 @@
* @param standy True if the device is entering standby, false if it's exiting standby.
*/
void setStandbyEnabled(boolean standby);
-
- /**
- * Start VR Input method for the given packageName in {@param componentName}.
- * This method notifies InputMethodManagerService to use VR IME instead of
- * regular phone IME.
- */
- void setVrInputMethod(in ComponentName componentName);
-
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 330d72f..42ac880 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -51,6 +51,7 @@
import android.view.AppTransitionAnimationSpec;
import android.view.WindowContentFrameStats;
import android.view.WindowManager;
+import android.view.SurfaceControl;
/**
* System private interface to the window manager.
@@ -555,8 +556,8 @@
* display content info to any SurfaceControl, as this would be a security issue.
*
* @param displayId The id of the display.
- * @param surfaceControlHandle The SurfaceControl handle that the top level layers for the
+ * @param surfaceControlHandle The SurfaceControl that the top level layers for the
* display should be re-parented to.
*/
- void reparentDisplayContent(int displayId, in IBinder surfaceControlHandle);
+ void reparentDisplayContent(int displayId, in SurfaceControl sc);
}
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index f3cb376..7fcb2af 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -185,6 +185,18 @@
}
/**
+ * Create a Surface assosciated with a given {@link SurfaceControl}. Buffers submitted to this
+ * surface will be displayed by the system compositor according to the parameters
+ * specified by the control. Multiple surfaces may be constructed from one SurfaceControl,
+ * but only one can be connected (e.g. have an active EGL context) at a time.
+ *
+ * @param from The SurfaceControl to assosciate this Surface with
+ */
+ public Surface(SurfaceControl from) {
+ copyFrom(from);
+ }
+
+ /**
* Create Surface from a {@link SurfaceTexture}.
*
* Images drawn to the Surface will be made available to the {@link
@@ -494,7 +506,6 @@
* in to it.
*
* @param other {@link SurfaceControl} to copy from.
- *
* @hide
*/
@UnsupportedAppUsage
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 5e98236..863b717 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -27,6 +27,10 @@
import static android.view.SurfaceControlProto.HASH_CODE;
import static android.view.SurfaceControlProto.NAME;
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.Size;
import android.annotation.UnsupportedAppUsage;
import android.graphics.Bitmap;
@@ -58,10 +62,16 @@
import java.io.Closeable;
/**
- * SurfaceControl
- * @hide
+ * Handle to an on-screen Surface managed by the system compositor. The SurfaceControl is
+ * a combination of a buffer source, and metadata about how to display the buffers.
+ * By constructing a {@link Surface} from this SurfaceControl you can submit buffers to be
+ * composited. Using {@link SurfaceControl.Transaction} you can manipulate various
+ * properties of how the buffer will be displayed on-screen. SurfaceControl's are
+ * arranged into a scene-graph like hierarchy, and as such any SurfaceControl may have
+ * a parent. Geometric properties like transform, crop, and Z-ordering will be inherited
+ * from the parent, as if the child were content in the parents buffer stream.
*/
-public class SurfaceControl implements Parcelable {
+public final class SurfaceControl implements Parcelable {
private static final String TAG = "SurfaceControl";
private static native long nativeCreate(SurfaceSession session, String name,
@@ -103,6 +113,8 @@
float dtdy, float dsdy);
private static native void nativeSetColorTransform(long transactionObj, long nativeObject,
float[] matrix, float[] translation);
+ private static native void nativeSetGeometry(long transactionObj, long nativeObject,
+ Rect sourceCrop, Rect dest, long orientation);
private static native void nativeSetColor(long transactionObj, long nativeObject, float[] color);
private static native void nativeSetFlags(long transactionObj, long nativeObject,
int flags, int mask);
@@ -156,7 +168,7 @@
private static native void nativeReparentChildren(long transactionObj, long nativeObject,
IBinder handle);
private static native void nativeReparent(long transactionObj, long nativeObject,
- IBinder parentHandle);
+ long newParentNativeObject);
private static native void nativeSeverChildren(long transactionObj, long nativeObject);
private static native void nativeSetOverrideScalingMode(long transactionObj, long nativeObject,
int scalingMode);
@@ -331,8 +343,7 @@
*/
public static final int BUILT_IN_DISPLAY_ID_HDMI = 1;
- /* Display power modes * /
-
+ // Display power modes.
/**
* Display power mode off: used while blanking the screen.
* Use only with {@link SurfaceControl#setDisplayPowerMode}.
@@ -403,7 +414,6 @@
/**
* Builder class for {@link SurfaceControl} objects.
- * @hide
*/
public static class Builder {
private SurfaceSession mSession;
@@ -427,8 +437,14 @@
}
/**
- * Construct a new {@link SurfaceControl} with the set parameters.
- * @hide
+ * Begin building a SurfaceControl.
+ */
+ public Builder() {
+ }
+
+ /**
+ * Construct a new {@link SurfaceControl} with the set parameters. The builder
+ * remains valid.
*/
public SurfaceControl build() {
if (mWidth < 0 || mHeight < 0) {
@@ -447,7 +463,6 @@
* Set a debugging-name for the SurfaceControl.
*
* @param name A name to identify the Surface in debugging.
- * @hide
*/
public Builder setName(String name) {
mName = name;
@@ -459,9 +474,9 @@
*
* @param width The buffer width in pixels.
* @param height The buffer height in pixels.
- * @hide
*/
- public Builder setBufferSize(int width, int height) {
+ public Builder setBufferSize(@IntRange(from = 0) int width,
+ @IntRange(from = 0) int height) {
if (width < 0 || height < 0) {
throw new IllegalArgumentException(
"width and height must be positive");
@@ -474,8 +489,8 @@
/**
* Set the pixel format of the controlled surface's buffers, using constants from
* {@link android.graphics.PixelFormat}.
- * @hide
*/
+ @NonNull
public Builder setFormat(@PixelFormat.Format int format) {
mFormat = format;
return this;
@@ -490,6 +505,7 @@
* @param protectedContent Whether to require a protected sink.
* @hide
*/
+ @NonNull
public Builder setProtected(boolean protectedContent) {
if (protectedContent) {
mFlags |= PROTECTED_APP;
@@ -506,6 +522,7 @@
* not a complete prevention of readback as {@link #setProtected}.
* @hide
*/
+ @NonNull
public Builder setSecure(boolean secure) {
if (secure) {
mFlags |= SECURE;
@@ -537,8 +554,8 @@
* If the underlying buffer lacks an alpha channel, it is as if setOpaque(true)
* were set automatically.
* @param opaque Whether the Surface is OPAQUE.
- * @hide
*/
+ @NonNull
public Builder setOpaque(boolean opaque) {
if (opaque) {
mFlags |= OPAQUE;
@@ -556,9 +573,9 @@
* of the parent.
*
* @param parent The parent control.
- * @hide
*/
- public Builder setParent(SurfaceControl parent) {
+ @NonNull
+ public Builder setParent(@Nullable SurfaceControl parent) {
mParent = parent;
return this;
}
@@ -673,9 +690,6 @@
private SurfaceControl(SurfaceSession session, String name, int w, int h, int format, int flags,
SurfaceControl parent, int windowType, int ownerUid)
throws OutOfResourcesException, IllegalArgumentException {
- if (session == null) {
- throw new IllegalArgumentException("session must not be null");
- }
if (name == null) {
throw new IllegalArgumentException("name must not be null");
}
@@ -729,9 +743,6 @@
mCloseGuard.open("release");
}
- /**
- * @hide
- */
public void readFromParcel(Parcel in) {
if (in == null) {
throw new IllegalArgumentException("source must not be null");
@@ -748,17 +759,11 @@
assignNativeObject(object);
}
- /**
- * @hide
- */
@Override
public int describeContents() {
return 0;
}
- /**
- * @hide
- */
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mName);
@@ -791,9 +796,6 @@
proto.end(token);
}
- /**
- * @hide
- */
public static final Creator<SurfaceControl> CREATOR
= new Creator<SurfaceControl>() {
public SurfaceControl createFromParcel(Parcel in) {
@@ -823,10 +825,12 @@
}
/**
- * Release the local reference to the server-side surface.
- * Always call release() when you're done with a Surface.
- * This will make the surface invalid.
- * @hide
+ * Release the local reference to the server-side surface. The surface
+ * may continue to exist on-screen as long as its parent continues
+ * to exist. To explicitly remove a surface from the screen use
+ * {@link Transaction#reparent} with a null-parent.
+ *
+ * Always call release() when you're done with a SurfaceControl.
*/
public void release() {
if (mNativeObject != 0) {
@@ -866,7 +870,10 @@
}
/**
- * @hide
+ * Check whether this instance points to a valid layer with the system-compositor. For
+ * example this may be false if construction failed, or the layer was released.
+ *
+ * @return Whether this SurfaceControl is valid.
*/
public boolean isValid() {
return mNativeObject != 0;
@@ -962,9 +969,9 @@
/**
* @hide
*/
- public void reparent(IBinder newParentHandle) {
+ public void reparent(SurfaceControl newParent) {
synchronized(SurfaceControl.class) {
- sGlobalTransaction.reparent(this, newParentHandle);
+ sGlobalTransaction.reparent(this, newParent);
}
}
@@ -1270,9 +1277,6 @@
}
}
- /**
- * @hide
- */
@Override
public String toString() {
return "Surface(name=" + mName + ")/@0x" +
@@ -1286,6 +1290,7 @@
/**
* Describes the properties of a physical display known to surface flinger.
+ * @hide
*/
public static final class PhysicalDisplayInfo {
/**
@@ -1777,9 +1782,12 @@
}
/**
- * @hide
+ * An atomic set of changes to a set of SurfaceControl.
*/
public static class Transaction implements Closeable {
+ /**
+ * @hide
+ */
public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
Transaction.class.getClassLoader(),
nativeGetNativeTransactionFinalizer(), 512);
@@ -1789,9 +1797,12 @@
Runnable mFreeNativeResources;
/**
- * @hide
+ * Open a new transaction object. The transaction may be filed with commands to
+ * manipulate {@link SurfaceControl} instances, and then applied atomically with
+ * {@link #apply}. Eventually the user should invoke {@link #close}, when the object
+ * is no longer required. Note however that re-using a transaction after a call to apply
+ * is allowed as a convenience.
*/
- @UnsupportedAppUsage
public Transaction() {
mNativeObject = nativeCreateTransaction();
mFreeNativeResources
@@ -1801,9 +1812,7 @@
/**
* Apply the transaction, clearing it's state, and making it usable
* as a new transaction.
- * @hide
*/
- @UnsupportedAppUsage
public void apply() {
apply(false);
}
@@ -1811,7 +1820,6 @@
/**
* Close the transaction, if the transaction was not already applied this will cancel the
* transaction.
- * @hide
*/
@Override
public void close() {
@@ -1841,6 +1849,27 @@
}
/**
+ * Toggle the visibility of a given Layer and it's sub-tree.
+ *
+ * @param sc The SurfaceControl for which to set the visibility
+ * @param visible The new visibility
+ * @return This transaction object.
+ */
+ @NonNull
+ public Transaction setVisibility(@NonNull SurfaceControl sc, boolean visible) {
+ sc.checkNotReleased();
+ if (visible) {
+ return show(sc);
+ } else {
+ return hide(sc);
+ }
+ }
+
+ /**
+ * Request that a given surface and it's sub-tree be shown.
+ *
+ * @param sc The surface to show.
+ * @return This transaction.
* @hide
*/
@UnsupportedAppUsage
@@ -1851,6 +1880,10 @@
}
/**
+ * Request that a given surface and it's sub-tree be hidden.
+ *
+ * @param sc The surface to hidden.
+ * @return This transaction.
* @hide
*/
@UnsupportedAppUsage
@@ -1871,10 +1904,17 @@
}
/**
- * @hide
+ * Set the default buffer size for the SurfaceControl, if there is an
+ * {@link Surface} assosciated with the control, then
+ * this will be the default size for buffers dequeued from it.
+ * @param sc The surface to set the buffer size for.
+ * @param w The default width
+ * @param h The default height
+ * @return This Transaction
*/
- @UnsupportedAppUsage
- public Transaction setBufferSize(SurfaceControl sc, int w, int h) {
+ @NonNull
+ public Transaction setBufferSize(@NonNull SurfaceControl sc,
+ @IntRange(from = 0) int w, @IntRange(from = 0) int h) {
sc.checkNotReleased();
mResizedSurfaces.put(sc, new Point(w, h));
nativeSetSize(mNativeObject, sc.mNativeObject, w, h);
@@ -1882,10 +1922,17 @@
}
/**
- * @hide
+ * Set the Z-order for a given SurfaceControl, relative to it's siblings.
+ * If two siblings share the same Z order the ordering is undefined. Surfaces
+ * with a negative Z will be placed below the parent surface.
+ *
+ * @param sc The SurfaceControl to set the Z order on
+ * @param z The Z-order
+ * @return This Transaction.
*/
- @UnsupportedAppUsage
- public Transaction setLayer(SurfaceControl sc, int z) {
+ @NonNull
+ public Transaction setLayer(@NonNull SurfaceControl sc,
+ @IntRange(from = Integer.MIN_VALUE, to = Integer.MAX_VALUE) int z) {
sc.checkNotReleased();
nativeSetLayer(mNativeObject, sc.mNativeObject, z);
return this;
@@ -1912,10 +1959,15 @@
}
/**
- * @hide
+ * Set the alpha for a given surface. If the alpha is non-zero the SurfaceControl
+ * will be blended with the Surfaces under it according to the specified ratio.
+ *
+ * @param sc The given SurfaceControl.
+ * @param alpha The alpha to set.
*/
- @UnsupportedAppUsage
- public Transaction setAlpha(SurfaceControl sc, float alpha) {
+ @NonNull
+ public Transaction setAlpha(@NonNull SurfaceControl sc,
+ @FloatRange(from = 0.0, to = 1.0) float alpha) {
sc.checkNotReleased();
nativeSetAlpha(mNativeObject, sc.mNativeObject, alpha);
return this;
@@ -1947,6 +1999,25 @@
}
/**
+ * Specify how the buffer assosciated with this Surface is mapped in to the
+ * parent coordinate space. The source frame will be scaled to fit the destination
+ * frame, after being rotated according to the orientation parameter.
+ *
+ * @param sc The SurfaceControl to specify the geometry of
+ * @param sourceCrop The source rectangle in buffer space. Or null for the entire buffer.
+ * @param destFrame The destination rectangle in parent space. Or null for the source frame.
+ * @param orientation The buffer rotation
+ * @return This transaction object.
+ */
+ @NonNull
+ public Transaction setGeometry(@NonNull SurfaceControl sc, @Nullable Rect sourceCrop,
+ @Nullable Rect destFrame, @Surface.Rotation int orientation) {
+ sc.checkNotReleased();
+ nativeSetGeometry(mNativeObject, sc.mNativeObject, sourceCrop, destFrame, orientation);
+ return this;
+ }
+
+ /**
* @hide
*/
@UnsupportedAppUsage
@@ -2023,20 +2094,20 @@
return this;
}
- @UnsupportedAppUsage
/**
* @hide
*/
+ @UnsupportedAppUsage
public Transaction setLayerStack(SurfaceControl sc, int layerStack) {
sc.checkNotReleased();
nativeSetLayerStack(mNativeObject, sc.mNativeObject, layerStack);
return this;
}
- @UnsupportedAppUsage
/**
* @hide
*/
+ @UnsupportedAppUsage
public Transaction deferTransactionUntil(SurfaceControl sc, IBinder handle,
long frameNumber) {
if (frameNumber < 0) {
@@ -2047,10 +2118,10 @@
return this;
}
- @UnsupportedAppUsage
/**
* @hide
*/
+ @UnsupportedAppUsage
public Transaction deferTransactionUntilSurface(SurfaceControl sc, Surface barrierSurface,
long frameNumber) {
if (frameNumber < 0) {
@@ -2071,13 +2142,25 @@
return this;
}
- /** Re-parents a specific child layer to a new parent
- * @hide
+ /**
+ * Re-parents a given layer to a new parent. Children inherit transform (position, scaling)
+ * crop, visibility, and Z-ordering from their parents, as if the children were pixels within the
+ * parent Surface.
+ *
+ * @param sc The SurfaceControl to reparent
+ * @param newParent The new parent for the given control.
+ * @return This Transaction
*/
- public Transaction reparent(SurfaceControl sc, IBinder newParentHandle) {
+ @NonNull
+ public Transaction reparent(@NonNull SurfaceControl sc,
+ @Nullable SurfaceControl newParent) {
sc.checkNotReleased();
- nativeReparent(mNativeObject, sc.mNativeObject,
- newParentHandle);
+ long otherObject = 0;
+ if (newParent != null) {
+ newParent.checkNotReleased();
+ otherObject = newParent.mNativeObject;
+ }
+ nativeReparent(mNativeObject, sc.mNativeObject, otherObject);
return this;
}
@@ -2245,9 +2328,12 @@
/**
* Merge the other transaction into this transaction, clearing the
* other transaction as if it had been applied.
- * @hide
+ *
+ * @param other The transaction to merge in to this one.
+ * @return This transaction.
*/
- public Transaction merge(Transaction other) {
+ @NonNull
+ public Transaction merge(@NonNull Transaction other) {
mResizedSurfaces.putAll(other.mResizedSurfaces);
other.mResizedSurfaces.clear();
nativeMergeTransaction(mNativeObject, other.mNativeObject);
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 61fb00d..45e6c50 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -120,10 +120,11 @@
final Rect mScreenRect = new Rect();
SurfaceSession mSurfaceSession;
- SurfaceControlWithBackground mSurfaceControl;
+ SurfaceControl mSurfaceControl;
// In the case of format changes we switch out the surface in-place
// we need to preserve the old one until the new one has drawn.
SurfaceControl mDeferredDestroySurfaceControl;
+ SurfaceControl mBackgroundControl;
final Rect mTmpRect = new Rect();
final Configuration mConfiguration = new Configuration();
@@ -487,6 +488,29 @@
}
}
+ private void updateBackgroundVisibilityInTransaction() {
+ if (mBackgroundControl == null) {
+ return;
+ }
+ if ((mSurfaceFlags & PixelFormat.OPAQUE) == 0) {
+ mBackgroundControl.show();
+ mBackgroundControl.setLayer(Integer.MIN_VALUE);
+ } else {
+ mBackgroundControl.hide();
+ }
+ }
+
+ private void releaseSurfaces() {
+ if (mSurfaceControl != null) {
+ mSurfaceControl.destroy();
+ mSurfaceControl = null;
+ }
+ if (mBackgroundControl != null) {
+ mBackgroundControl.destroy();
+ mBackgroundControl = null;
+ }
+ }
+
/** @hide */
protected void updateSurface() {
if (!mHaveFrame) {
@@ -553,14 +577,21 @@
updateOpaqueFlag();
final String name = "SurfaceView - " + viewRoot.getTitle().toString();
- mSurfaceControl = new SurfaceControlWithBackground(
- name,
- (mSurfaceFlags & SurfaceControl.OPAQUE) != 0,
- new SurfaceControl.Builder(mSurfaceSession)
- .setBufferSize(mSurfaceWidth, mSurfaceHeight)
- .setFormat(mFormat)
- .setParent(viewRoot.getSurfaceControl())
- .setFlags(mSurfaceFlags));
+ mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
+ .setName(name)
+ .setOpaque((mSurfaceFlags & SurfaceControl.OPAQUE) != 0)
+ .setBufferSize(mSurfaceWidth, mSurfaceHeight)
+ .setFormat(mFormat)
+ .setParent(viewRoot.getSurfaceControl())
+ .setFlags(mSurfaceFlags)
+ .build();
+ mBackgroundControl = new SurfaceControl.Builder(mSurfaceSession)
+ .setName("Background for -" + name)
+ .setOpaque(true)
+ .setColorLayer(true)
+ .setParent(mSurfaceControl)
+ .build();
+
} else if (mSurfaceControl == null) {
return;
}
@@ -577,11 +608,13 @@
SurfaceControl.openTransaction();
try {
mSurfaceControl.setLayer(mSubLayer);
+
if (mViewVisibility) {
mSurfaceControl.show();
} else {
mSurfaceControl.hide();
}
+ updateBackgroundVisibilityInTransaction();
// While creating the surface, we will set it's initial
// geometry. Outside of that though, we should generally
@@ -724,8 +757,7 @@
if (mSurfaceControl != null && !mSurfaceCreated) {
mSurface.release();
- mSurfaceControl.destroy();
- mSurfaceControl = null;
+ releaseSurfaces();
}
}
} catch (Exception ex) {
@@ -823,7 +855,6 @@
final ViewRootImpl viewRoot = getViewRootImpl();
applySurfaceTransforms(mSurfaceControl, position, frameNumber);
- applySurfaceTransforms(mSurfaceControl.mBackgroundControl, position, frameNumber);
applyChildSurfaceTransaction_renderWorker(mRtTransaction, viewRoot.mSurface,
frameNumber);
@@ -950,7 +981,19 @@
* @hide
*/
public void setResizeBackgroundColor(int bgColor) {
- mSurfaceControl.setBackgroundColor(bgColor);
+ if (mBackgroundControl == null) {
+ return;
+ }
+
+ final float[] colorComponents = new float[] { Color.red(bgColor) / 255.f,
+ Color.green(bgColor) / 255.f, Color.blue(bgColor) / 255.f };
+
+ SurfaceControl.openTransaction();
+ try {
+ mBackgroundControl.setColor(colorComponents);
+ } finally {
+ SurfaceControl.closeTransaction();
+ }
}
@UnsupportedAppUsage
@@ -1128,154 +1171,12 @@
};
/**
- * @hide
+ * Return a SurfaceControl which can be used for parenting Surfaces to
+ * this SurfaceView.
+ *
+ * @return The SurfaceControl for this SurfaceView.
*/
public SurfaceControl getSurfaceControl() {
return mSurfaceControl;
}
-
- class SurfaceControlWithBackground extends SurfaceControl {
- SurfaceControl mBackgroundControl;
- private boolean mOpaque = true;
- public boolean mVisible = false;
-
- public SurfaceControlWithBackground(String name, boolean opaque, SurfaceControl.Builder b)
- throws Exception {
- super(b.setName(name).build());
-
- mBackgroundControl = b.setName("Background for -" + name)
- .setFormat(OPAQUE)
- // Unset the buffer size of the background color layer.
- .setBufferSize(0, 0)
- .setColorLayer(true)
- .build();
- mOpaque = opaque;
- }
-
- @Override
- public void setAlpha(float alpha) {
- super.setAlpha(alpha);
- mBackgroundControl.setAlpha(alpha);
- }
-
- @Override
- public void setLayer(int zorder) {
- super.setLayer(zorder);
- // -3 is below all other child layers as SurfaceView never goes below -2
- mBackgroundControl.setLayer(-3);
- }
-
- @Override
- public void setPosition(float x, float y) {
- super.setPosition(x, y);
- mBackgroundControl.setPosition(x, y);
- }
-
- @Override
- public void setBufferSize(int w, int h) {
- super.setBufferSize(w, h);
- // The background surface is a color layer so we do not set a size.
- }
-
- @Override
- public void setWindowCrop(Rect crop) {
- super.setWindowCrop(crop);
- mBackgroundControl.setWindowCrop(crop);
- }
-
- @Override
- public void setWindowCrop(int width, int height) {
- super.setWindowCrop(width, height);
- mBackgroundControl.setWindowCrop(width, height);
- }
-
- @Override
- public void setLayerStack(int layerStack) {
- super.setLayerStack(layerStack);
- mBackgroundControl.setLayerStack(layerStack);
- }
-
- @Override
- public void setOpaque(boolean isOpaque) {
- super.setOpaque(isOpaque);
- mOpaque = isOpaque;
- updateBackgroundVisibility();
- }
-
- @Override
- public void setSecure(boolean isSecure) {
- super.setSecure(isSecure);
- }
-
- @Override
- public void setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
- super.setMatrix(dsdx, dtdx, dsdy, dtdy);
- mBackgroundControl.setMatrix(dsdx, dtdx, dsdy, dtdy);
- }
-
- @Override
- public void hide() {
- super.hide();
- mVisible = false;
- updateBackgroundVisibility();
- }
-
- @Override
- public void show() {
- super.show();
- mVisible = true;
- updateBackgroundVisibility();
- }
-
- @Override
- public void destroy() {
- super.destroy();
- mBackgroundControl.destroy();
- }
-
- @Override
- public void release() {
- super.release();
- mBackgroundControl.release();
- }
-
- @Override
- public void setTransparentRegionHint(Region region) {
- super.setTransparentRegionHint(region);
- mBackgroundControl.setTransparentRegionHint(region);
- }
-
- @Override
- public void deferTransactionUntil(IBinder handle, long frame) {
- super.deferTransactionUntil(handle, frame);
- mBackgroundControl.deferTransactionUntil(handle, frame);
- }
-
- @Override
- public void deferTransactionUntil(Surface barrier, long frame) {
- super.deferTransactionUntil(barrier, frame);
- mBackgroundControl.deferTransactionUntil(barrier, frame);
- }
-
- /** Set the color to fill the background with. */
- private void setBackgroundColor(int bgColor) {
- final float[] colorComponents = new float[] { Color.red(bgColor) / 255.f,
- Color.green(bgColor) / 255.f, Color.blue(bgColor) / 255.f };
-
- SurfaceControl.openTransaction();
- try {
- mBackgroundControl.setColor(colorComponents);
- } finally {
- SurfaceControl.closeTransaction();
- }
- }
-
- void updateBackgroundVisibility() {
- if (mOpaque && mVisible) {
- mBackgroundControl.show();
- } else {
- mBackgroundControl.hide();
- }
- }
- }
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 86c5f18..10b99ec 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -922,19 +922,6 @@
}
}
- /**
- * Returns a list of VR InputMethod currently installed.
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS)
- public List<InputMethodInfo> getVrInputMethodList() {
- try {
- return mService.getVrInputMethodList();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
public List<InputMethodInfo> getEnabledInputMethodList() {
try {
return mService.getEnabledInputMethodList();
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 356d178..8194a92 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -35,7 +35,6 @@
// TODO: Use ParceledListSlice instead
List<InputMethodInfo> getInputMethodList();
- List<InputMethodInfo> getVrInputMethodList();
// TODO: Use ParceledListSlice instead
List<InputMethodInfo> getEnabledInputMethodList();
List<InputMethodSubtype> getEnabledInputMethodSubtypeList(in String imiId,
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 897427f..0453195 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -126,7 +126,12 @@
jstring nameStr, jint w, jint h, jint format, jint flags, jlong parentObject,
jint windowType, jint ownerUid) {
ScopedUtfChars name(env, nameStr);
- sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj));
+ sp<SurfaceComposerClient> client;
+ if (sessionObj != NULL) {
+ client = android_view_SurfaceSession_getClient(env, sessionObj);
+ } else {
+ client = SurfaceComposerClient::getDefault();
+ }
SurfaceControl *parent = reinterpret_cast<SurfaceControl*>(parentObject);
sp<SurfaceControl> surface;
status_t err = client->createSurfaceChecked(
@@ -277,6 +282,21 @@
transaction->setPosition(ctrl, x, y);
}
+static void nativeSetGeometry(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject,
+ jobject sourceObj, jobject dstObj, jlong orientation) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+ SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
+
+ Rect source, dst;
+ if (sourceObj != NULL) {
+ source = rectFromObj(env, sourceObj);
+ }
+ if (dstObj != NULL) {
+ dst = rectFromObj(env, dstObj);
+ }
+ transaction->setGeometry(ctrl, source, dst, orientation);
+}
+
static void nativeSetGeometryAppliesWithResize(JNIEnv* env, jclass clazz,
jlong transactionObj,
jlong nativeObject) {
@@ -868,13 +888,13 @@
static void nativeReparent(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject,
- jobject newParentObject) {
+ jlong newParentObject) {
auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
- sp<IBinder> parentHandle = ibinderForJavaObject(env, newParentObject);
+ auto newParent = reinterpret_cast<SurfaceControl *>(newParentObject);
{
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
- transaction->reparent(ctrl, parentHandle);
+ transaction->reparent(ctrl, newParent != NULL ? newParent->getHandle() : NULL);
}
}
@@ -1063,7 +1083,7 @@
(void*)nativeDeferTransactionUntilSurface },
{"nativeReparentChildren", "(JJLandroid/os/IBinder;)V",
(void*)nativeReparentChildren } ,
- {"nativeReparent", "(JJLandroid/os/IBinder;)V",
+ {"nativeReparent", "(JJJ)V",
(void*)nativeReparent },
{"nativeSeverChildren", "(JJ)V",
(void*)nativeSeverChildren } ,
@@ -1087,6 +1107,8 @@
{"nativeGetDisplayedContentSample",
"(Landroid/os/IBinder;JJ)Landroid/hardware/display/DisplayedContentSample;",
(void*)nativeGetDisplayedContentSample },
+ {"nativeSetGeometry", "(JJLandroid/graphics/Rect;Landroid/graphics/Rect;J)V",
+ (void*)nativeSetGeometry }
};
int register_android_view_SurfaceControl(JNIEnv* env)
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 7028008..2e7184b 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -493,10 +493,7 @@
// Apply system or app filter based on uid.
if (uid >= AID_APP_START) {
if (is_child_zygote) {
- // set_app_zygote_seccomp_filter();
- // TODO(b/111434506) install the filter; for now, install the app filter
- // which is more restrictive.
- set_app_seccomp_filter();
+ set_app_zygote_seccomp_filter();
} else {
set_app_seccomp_filter();
}
@@ -1645,14 +1642,10 @@
return;
}
- // TODO(b/111434506) install the filter
-
- /*
bool installed = install_setuidgid_seccomp_filter(uidGidMin, uidGidMax);
if (!installed) {
RuntimeAbort(env, __LINE__, "Could not install setuid/setgid seccomp filter.");
}
- */
}
/**
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ea0c8e2..96b8dc2 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1748,6 +1748,10 @@
<permission android:name="android.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED"
android:protectionLevel="signature" />
+ <!-- @hide Allows the device to be reset, clearing all data and enables Test Harness Mode. -->
+ <permission android:name="android.permission.ENABLE_TEST_HARNESS_MODE"
+ android:protectionLevel="signature" />
+
<!-- ================================== -->
<!-- Permissions for accessing accounts -->
<!-- ================================== -->
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
index 28e6637..f7ce038 100644
--- a/media/java/android/media/MediaController2.java
+++ b/media/java/android/media/MediaController2.java
@@ -71,6 +71,8 @@
private final Object mLock = new Object();
//@GuardedBy("mLock")
+ private boolean mClosed;
+ //@GuardedBy("mLock")
private int mNextSeqNumber;
//@GuardedBy("mLock")
private Session2Link mSessionBinder;
@@ -141,7 +143,14 @@
@Override
public void close() {
synchronized (mLock) {
+ if (mClosed) {
+ // Already closed. Ignore rest of clean up code.
+ // Note: unbindService() throws IllegalArgumentException when it's called twice.
+ return;
+ }
+ mClosed = true;
if (mServiceConnection != null) {
+ // Note: This should be called even when the bindService() has returned false.
mContext.unbindService(mServiceConnection);
}
if (mSessionBinder != null) {
@@ -167,7 +176,7 @@
* If it is not connected yet, it returns {@code null}.
* <p>
* This may differ with the {@link Session2Token} from the constructor. For example, if the
- * controller is created with the token for MediaSession2Service, this would return
+ * controller is created with the token for {@link MediaSession2Service}, this would return
* token for the {@link MediaSession2} in the service.
*
* @return Session2Token of the connected session, or {@code null} if not connected
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
index 4ec25ce..fdd07fd 100644
--- a/media/java/android/media/MediaSession2.java
+++ b/media/java/android/media/MediaSession2.java
@@ -90,6 +90,8 @@
private boolean mClosed;
//@GuardedBy("mLock")
private boolean mPlaybackActive;
+ //@GuardedBy("mLock")
+ private ForegroundServiceEventCallback mForegroundServiceEventCallback;
MediaSession2(@NonNull Context context, @NonNull String id, PendingIntent sessionActivity,
@NonNull Executor callbackExecutor, @NonNull SessionCallback callback) {
@@ -119,6 +121,7 @@
public void close() {
try {
List<ControllerInfo> controllerInfos;
+ ForegroundServiceEventCallback callback;
synchronized (mLock) {
if (mClosed) {
return;
@@ -126,11 +129,15 @@
mClosed = true;
controllerInfos = getConnectedControllers();
mConnectedControllers.clear();
- mCallback.onSessionClosed(this);
+ callback = mForegroundServiceEventCallback;
+ mForegroundServiceEventCallback = null;
}
synchronized (MediaSession2.class) {
SESSION_ID_LIST.remove(mSessionId);
}
+ if (callback != null) {
+ callback.onSessionClosed(this);
+ }
for (ControllerInfo info : controllerInfos) {
info.notifyDisconnected();
}
@@ -224,11 +231,16 @@
* @param playbackActive {@code true} if the playback active, {@code false} otherwise.
**/
public void setPlaybackActive(boolean playbackActive) {
+ final ForegroundServiceEventCallback serviceCallback;
synchronized (mLock) {
if (mPlaybackActive == playbackActive) {
return;
}
mPlaybackActive = playbackActive;
+ serviceCallback = mForegroundServiceEventCallback;
+ }
+ if (serviceCallback != null) {
+ serviceCallback.onPlaybackActiveChanged(this, playbackActive);
}
List<ControllerInfo> controllerInfos = getConnectedControllers();
for (ControllerInfo controller : controllerInfos) {
@@ -257,6 +269,18 @@
return mCallback;
}
+ void setForegroundServiceEventCallback(ForegroundServiceEventCallback callback) {
+ synchronized (mLock) {
+ if (mForegroundServiceEventCallback == callback) {
+ return;
+ }
+ if (mForegroundServiceEventCallback != null && callback != null) {
+ throw new IllegalStateException("A session cannot be added to multiple services");
+ }
+ mForegroundServiceEventCallback = callback;
+ }
+ }
+
// Called by Session2Link.onConnect and MediaSession2Service.MediaSession2ServiceStub.connect
void onConnect(final Controller2Link controller, int callingPid, int callingUid, int seq,
Bundle connectionRequest) {
@@ -695,8 +719,6 @@
* This API is not generally intended for third party application developers.
*/
public abstract static class SessionCallback {
- ForegroundServiceEventCallback mForegroundServiceEventCallback;
-
/**
* Called when a controller is created for this session. Return allowed commands for
* controller. By default it returns {@code null}.
@@ -753,19 +775,10 @@
public void onCommandResult(@NonNull MediaSession2 session,
@NonNull ControllerInfo controller, @NonNull Object token,
@NonNull Session2Command command, @NonNull Session2Command.Result result) {}
+ }
- final void onSessionClosed(MediaSession2 session) {
- if (mForegroundServiceEventCallback != null) {
- mForegroundServiceEventCallback.onSessionClosed(session);
- }
- }
-
- void setForegroundServiceEventCallback(ForegroundServiceEventCallback callback) {
- mForegroundServiceEventCallback = callback;
- }
-
- abstract static class ForegroundServiceEventCallback {
- public void onSessionClosed(MediaSession2 session) {}
- }
+ abstract static class ForegroundServiceEventCallback {
+ public void onPlaybackActiveChanged(MediaSession2 session, boolean playbackActive) {}
+ public void onSessionClosed(MediaSession2 session) {}
}
}
diff --git a/media/java/android/media/MediaSession2Service.java b/media/java/android/media/MediaSession2Service.java
index 8fb00fe..5bb746a 100644
--- a/media/java/android/media/MediaSession2Service.java
+++ b/media/java/android/media/MediaSession2Service.java
@@ -19,7 +19,10 @@
import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.Notification;
+import android.app.NotificationManager;
import android.app.Service;
+import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.Bundle;
@@ -28,8 +31,6 @@
import android.util.ArrayMap;
import android.util.Log;
-import com.android.internal.annotations.GuardedBy;
-
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -42,11 +43,7 @@
* Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
* <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
* for consistent behavior across all devices.
- * @hide
*/
-// TODO: Unhide
-// TODO: Add onUpdateNotification(), and calls it to get Notification for startForegroundService()
-// when a session's player state becomes playing.
public abstract class MediaSession2Service extends Service {
/**
* The {@link Intent} that must be declared as handled by the service.
@@ -56,10 +53,29 @@
private static final String TAG = "MediaSession2Service";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private final Object mLock = new Object();
- @GuardedBy("mLock")
- private Map<String, MediaSession2> mSessions = new ArrayMap<>();
+ private final MediaSession2.ForegroundServiceEventCallback mForegroundServiceEventCallback =
+ new MediaSession2.ForegroundServiceEventCallback() {
+ @Override
+ public void onPlaybackActiveChanged(MediaSession2 session, boolean playbackActive) {
+ MediaSession2Service.this.onPlaybackActiveChanged(session, playbackActive);
+ }
+ @Override
+ public void onSessionClosed(MediaSession2 session) {
+ removeSession(session);
+ }
+ };
+
+ private final Object mLock = new Object();
+ //@GuardedBy("mLock")
+ private NotificationManager mNotificationManager;
+ //@GuardedBy("mLock")
+ private Intent mStartSelfIntent;
+ //@GuardedBy("mLock")
+ private Map<String, MediaSession2> mSessions = new ArrayMap<>();
+ //@GuardedBy("mLock")
+ private Map<MediaSession2, MediaNotification> mNotifications = new ArrayMap<>();
+ //@GuardedBy("mLock")
private MediaSession2ServiceStub mStub;
/**
@@ -72,7 +88,12 @@
@Override
public void onCreate() {
super.onCreate();
- mStub = new MediaSession2ServiceStub(this);
+ synchronized (mLock) {
+ mStub = new MediaSession2ServiceStub(this);
+ mStartSelfIntent = new Intent(this, this.getClass());
+ mNotificationManager =
+ (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ }
}
@CallSuper
@@ -80,18 +101,13 @@
@Nullable
public IBinder onBind(@NonNull Intent intent) {
if (SERVICE_INTERFACE.equals(intent.getAction())) {
- return mStub;
+ synchronized (mLock) {
+ return mStub;
+ }
}
return null;
}
- @CallSuper
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- // TODO: Dispatch media key events to the primary session.
- return START_STICKY;
- }
-
/**
* Called by the system to notify that it is no longer used and is being removed. Do not call
* this method directly.
@@ -104,10 +120,12 @@
public void onDestroy() {
super.onDestroy();
synchronized (mLock) {
- for (MediaSession2 session : mSessions.values()) {
- session.getCallback().setForegroundServiceEventCallback(null);
+ List<MediaSession2> sessions = getSessions();
+ for (MediaSession2 session : sessions) {
+ removeSession(session);
}
mSessions.clear();
+ mNotifications.clear();
}
mStub.close();
}
@@ -144,6 +162,24 @@
public abstract MediaSession2 onGetPrimarySession();
/**
+ * Called when notification UI needs update. Override this method to show or cancel your own
+ * notification UI.
+ * <p>
+ * This would be called on {@link MediaSession2}'s callback executor when playback state is
+ * changed.
+ * <p>
+ * With the notification returned here, the service becomes foreground service when the playback
+ * is started. Apps must request the permission
+ * {@link android.Manifest.permission#FOREGROUND_SERVICE} in order to use this API. It becomes
+ * background service after the playback is stopped.
+ *
+ * @param session a session that needs notification update.
+ * @return a {@link MediaNotification}. Can be {@code null}.
+ */
+ @Nullable
+ public abstract MediaNotification onUpdateNotification(@NonNull MediaSession2 session);
+
+ /**
* Adds a session to this service.
* <p>
* Added session will be removed automatically when it's closed, or removed when
@@ -161,21 +197,15 @@
}
synchronized (mLock) {
MediaSession2 previousSession = mSessions.get(session.getSessionId());
- if (previousSession != session) {
- if (previousSession != null) {
+ if (previousSession != null) {
+ if (previousSession != session) {
Log.w(TAG, "Session ID should be unique, ID=" + session.getSessionId()
+ ", previous=" + previousSession + ", session=" + session);
}
return;
}
mSessions.put(session.getSessionId(), session);
- session.getCallback().setForegroundServiceEventCallback(
- new MediaSession2.SessionCallback.ForegroundServiceEventCallback() {
- @Override
- public void onSessionClosed(MediaSession2 session) {
- removeSession(session);
- }
- });
+ session.setForegroundServiceEventCallback(mForegroundServiceEventCallback);
}
}
@@ -189,8 +219,21 @@
if (session == null) {
throw new IllegalArgumentException("session shouldn't be null");
}
+ MediaNotification notification;
synchronized (mLock) {
+ if (mSessions.get(session.getSessionId()) != session) {
+ // Session isn't added or removed already.
+ return;
+ }
mSessions.remove(session.getSessionId());
+ notification = mNotifications.remove(session);
+ }
+ session.setForegroundServiceEventCallback(null);
+ if (notification != null) {
+ mNotificationManager.cancel(notification.getNotificationId());
+ }
+ if (getSessions().isEmpty()) {
+ stopForeground(false);
}
}
@@ -207,6 +250,78 @@
return list;
}
+ /**
+ * Called by registered {@link MediaSession2.ForegroundServiceEventCallback}
+ *
+ * @param session session with change
+ * @param playbackActive {@code true} if playback is active.
+ */
+ void onPlaybackActiveChanged(MediaSession2 session, boolean playbackActive) {
+ MediaNotification mediaNotification = onUpdateNotification(session);
+ if (mediaNotification == null) {
+ // The service implementation doesn't want to use the automatic start/stopForeground
+ // feature.
+ return;
+ }
+ synchronized (mLock) {
+ mNotifications.put(session, mediaNotification);
+ }
+ int id = mediaNotification.getNotificationId();
+ Notification notification = mediaNotification.getNotification();
+ if (!playbackActive) {
+ mNotificationManager.notify(id, notification);
+ return;
+ }
+ // playbackActive == true
+ startForegroundService(mStartSelfIntent);
+ startForeground(id, notification);
+ }
+
+ /**
+ * Returned by {@link #onUpdateNotification(MediaSession2)} for making session service
+ * foreground service to keep playback running in the background. It's highly recommended to
+ * show media style notification here.
+ */
+ public static class MediaNotification {
+ private final int mNotificationId;
+ private final Notification mNotification;
+
+ /**
+ * Default constructor
+ *
+ * @param notificationId notification id to be used for
+ * {@link NotificationManager#notify(int, Notification)}.
+ * @param notification a notification to make session service run in the foreground. Media
+ * style notification is recommended here.
+ */
+ public MediaNotification(int notificationId, @NonNull Notification notification) {
+ if (notification == null) {
+ throw new IllegalArgumentException("notification shouldn't be null");
+ }
+ mNotificationId = notificationId;
+ mNotification = notification;
+ }
+
+ /**
+ * Gets the id of the notification.
+ *
+ * @return the notification id
+ */
+ public int getNotificationId() {
+ return mNotificationId;
+ }
+
+ /**
+ * Gets the notification.
+ *
+ * @return the notification
+ */
+ @NonNull
+ public Notification getNotification() {
+ return mNotification;
+ }
+ }
+
private static final class MediaSession2ServiceStub extends IMediaSession2Service.Stub
implements AutoCloseable {
final WeakReference<MediaSession2Service> mService;
diff --git a/media/java/android/media/Session2Token.java b/media/java/android/media/Session2Token.java
index d8f74c5..023ee46 100644
--- a/media/java/android/media/Session2Token.java
+++ b/media/java/android/media/Session2Token.java
@@ -48,14 +48,6 @@
* <p>
* It can be also obtained by {@link android.media.session.MediaSessionManager}.
*/
-// New version of MediaSession2.Token for following reasons
-// - Stop implementing Parcelable for updatable support
-// - Represent session and library service (formerly browser service) in one class.
-// Previously MediaSession2.Token was for session and ComponentName was for service.
-// This helps controller apps to keep target of dispatching media key events in uniform way.
-// For details about the reason, see following. (Android O+)
-// android.media.session.MediaSessionManager.Callback#onAddressedPlayerChanged
-// TODO: use @link for MediaSession2Service
public final class Session2Token implements Parcelable {
private static final String TAG = "Session2Token";
@@ -85,12 +77,13 @@
public static final int TYPE_SESSION = 0;
/**
- * Type for MediaSession2Service.
+ * Type for {@link MediaSession2Service}.
*/
public static final int TYPE_SESSION_SERVICE = 1;
private final int mUid;
- private final @TokenType int mType;
+ @TokenType
+ private final int mType;
private final String mPackageName;
private final String mServiceName;
private final Session2Link mSessionLink;
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index b903142..c3c3f25 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -166,6 +166,8 @@
<uses-permission android:name="android.permission.CONTROL_KEYGUARD" />
<uses-permission android:name="android.permission.SUSPEND_APPS" />
<uses-permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" />
+ <!-- Permission needed to wipe the device for Test Harness Mode -->
+ <uses-permission android:name="android.permission.ENABLE_TEST_HARNESS_MODE" />
<uses-permission android:name="android.permission.MANAGE_APPOPS" />
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
index 35abfd4..49db488 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
@@ -676,6 +676,10 @@
// 3tap and hold
afterLongTapTimeoutTransitionToDraggingState(event);
+ } else if (isTapOutOfDistanceSlop()) {
+
+ transitionToDelegatingStateAndClear();
+
} else if (mDetectTripleTap
// If magnified, delay an ACTION_DOWN for mMultiTapMaxDelay
// to ensure reachability of
@@ -921,6 +925,31 @@
// TODO: multi-display support for magnification gesture handler
mMagnificationController.setForceShowMagnifiableBounds(Display.DEFAULT_DISPLAY, state);
}
+
+ /**
+ * Detects if last action down is out of distance slop between with previous
+ * one, when triple tap is enabled.
+ *
+ * @return true if tap is out of distance slop
+ */
+ boolean isTapOutOfDistanceSlop() {
+ if (!mDetectTripleTap) return false;
+ if (mPreLastDown == null || mLastDown == null) {
+ return false;
+ }
+ final boolean outOfDistanceSlop =
+ GestureUtils.distance(mPreLastDown, mLastDown) > mMultiTapMaxDistance;
+ if (tapCount() > 0) {
+ return outOfDistanceSlop;
+ }
+ // There's no tap in the queue here. We still need to check if this is the case that
+ // user tap screen quickly and out of distance slop.
+ if (outOfDistanceSlop
+ && !GestureUtils.isTimedOut(mPreLastDown, mLastDown, mMultiTapMaxDelay)) {
+ return true;
+ }
+ return false;
+ }
}
private void zoomOn(float centerX, float centerY) {
diff --git a/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java b/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java
index 1e9a007..190fff1 100644
--- a/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java
+++ b/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java
@@ -31,6 +31,19 @@
*/
byte[] getFrpCredentialHandle();
+ /** Stores the data used to enable the Test Harness Mode after factory-resetting. */
+ void setTestHarnessModeData(byte[] data);
+
+ /**
+ * Retrieves the data used to place the device into Test Harness Mode.
+ *
+ * @throws IllegalStateException if the underlying storage is corrupt or inaccessible.
+ */
+ byte[] getTestHarnessModeData();
+
+ /** Clear out the Test Harness Mode data. */
+ void clearTestHarnessModeData();
+
/** Update the OEM unlock enabled bit, bypassing user restriction checks. */
void forceOemUnlockEnabled(boolean enabled);
}
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index 21093b9..bd5ad96 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -16,6 +16,8 @@
package com.android.server;
+import static com.android.internal.util.Preconditions.checkArgument;
+
import android.Manifest;
import android.app.ActivityManager;
import android.content.Context;
@@ -28,12 +30,10 @@
import android.os.UserManager;
import android.service.persistentdata.IPersistentDataBlockService;
import android.service.persistentdata.PersistentDataBlockManager;
-import android.util.Log;
import android.util.Slog;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
import libcore.io.IoUtils;
@@ -65,6 +65,40 @@
*
* Clients can read any number of bytes from the currently written block up to its total size by
* invoking {@link IPersistentDataBlockService#read}
+ *
+ * The persistent data block is currently laid out as follows:
+ * | ---------BEGINNING OF PARTITION-------------|
+ * | Partition digest (32 bytes) |
+ * | --------------------------------------------|
+ * | PARTITION_TYPE_MARKER (4 bytes) |
+ * | --------------------------------------------|
+ * | FRP data block length (4 bytes) |
+ * | --------------------------------------------|
+ * | FRP data (variable length) |
+ * | --------------------------------------------|
+ * | ... |
+ * | --------------------------------------------|
+ * | Test mode data block (10000 bytes) |
+ * | --------------------------------------------|
+ * | | Test mode data length (4 bytes) |
+ * | --------------------------------------------|
+ * | | Test mode data (variable length) |
+ * | | ... |
+ * | --------------------------------------------|
+ * | FRP credential handle block (1000 bytes) |
+ * | --------------------------------------------|
+ * | | FRP credential handle length (4 bytes)|
+ * | --------------------------------------------|
+ * | | FRP credential handle (variable len) |
+ * | | ... |
+ * | --------------------------------------------|
+ * | OEM Unlock bit (1 byte) |
+ * | ---------END OF PARTITION-------------------|
+ *
+ * TODO: now that the persistent partition contains several blocks, next time someone wants a new
+ * block, we should look at adding more generic block definitions and get rid of the various raw
+ * XXX_RESERVED_SIZE and XXX_DATA_SIZE constants. That will ensure the code is easier to maintain
+ * and less likely to introduce out-of-bounds read/write.
*/
public class PersistentDataBlockService extends SystemService {
private static final String TAG = PersistentDataBlockService.class.getSimpleName();
@@ -73,10 +107,16 @@
private static final int HEADER_SIZE = 8;
// Magic number to mark block device as adhering to the format consumed by this service
private static final int PARTITION_TYPE_MARKER = 0x19901873;
- /** Size of the block reserved for FPR credential, including 4 bytes for the size header. */
+ /** Size of the block reserved for FRP credential, including 4 bytes for the size header. */
private static final int FRP_CREDENTIAL_RESERVED_SIZE = 1000;
/** Maximum size of the FRP credential handle that can be stored. */
private static final int MAX_FRP_CREDENTIAL_HANDLE_SIZE = FRP_CREDENTIAL_RESERVED_SIZE - 4;
+ /**
+ * Size of the block reserved for Test Harness Mode data, including 4 bytes for the size header.
+ */
+ private static final int TEST_MODE_RESERVED_SIZE = 10000;
+ /** Maximum size of the Test Harness Mode data that can be stored. */
+ private static final int MAX_TEST_MODE_DATA_SIZE = TEST_MODE_RESERVED_SIZE - 4;
// Limit to 100k as blocks larger than this might cause strain on Binder.
private static final int MAX_DATA_BLOCK_SIZE = 1024 * 100;
@@ -221,6 +261,14 @@
return mBlockDeviceSize;
}
+ private long getFrpCredentialDataOffset() {
+ return getBlockDeviceSize() - 1 - FRP_CREDENTIAL_RESERVED_SIZE;
+ }
+
+ private long getTestHarnessModeDataOffset() {
+ return getFrpCredentialDataOffset() - TEST_MODE_RESERVED_SIZE;
+ }
+
private boolean enforceChecksumValidity() {
byte[] storedDigest = new byte[DIGEST_SIZE_BYTES];
@@ -383,7 +431,7 @@
private long doGetMaximumDataBlockSize() {
long actualSize = getBlockDeviceSize() - HEADER_SIZE - DIGEST_SIZE_BYTES
- - FRP_CREDENTIAL_RESERVED_SIZE - 1;
+ - TEST_MODE_RESERVED_SIZE - FRP_CREDENTIAL_RESERVED_SIZE - 1;
return actualSize <= MAX_DATA_BLOCK_SIZE ? actualSize : MAX_DATA_BLOCK_SIZE;
}
@@ -391,6 +439,13 @@
private native int nativeWipe(String path);
private final IBinder mService = new IPersistentDataBlockService.Stub() {
+
+ /**
+ * Write the data to the persistent data block.
+ *
+ * @return a positive integer of the number of bytes that were written if successful,
+ * otherwise a negative integer indicating there was a problem
+ */
@Override
public int write(byte[] data) throws RemoteException {
enforceUid(Binder.getCallingUid());
@@ -597,12 +652,51 @@
@Override
public void setFrpCredentialHandle(byte[] handle) {
- Preconditions.checkArgument(handle == null || handle.length > 0,
- "handle must be null or non-empty");
- Preconditions.checkArgument(handle == null
- || handle.length <= MAX_FRP_CREDENTIAL_HANDLE_SIZE,
- "handle must not be longer than " + MAX_FRP_CREDENTIAL_HANDLE_SIZE);
+ writeInternal(handle, getFrpCredentialDataOffset(), MAX_FRP_CREDENTIAL_HANDLE_SIZE);
+ }
+ @Override
+ public byte[] getFrpCredentialHandle() {
+ return readInternal(getFrpCredentialDataOffset(), MAX_FRP_CREDENTIAL_HANDLE_SIZE);
+ }
+
+ @Override
+ public void setTestHarnessModeData(byte[] data) {
+ writeInternal(data, getTestHarnessModeDataOffset(), MAX_TEST_MODE_DATA_SIZE);
+ }
+
+ @Override
+ public byte[] getTestHarnessModeData() {
+ byte[] data = readInternal(getTestHarnessModeDataOffset(), MAX_TEST_MODE_DATA_SIZE);
+ if (data == null) {
+ return new byte[0];
+ }
+ return data;
+ }
+
+ @Override
+ public void clearTestHarnessModeData() {
+ int size = Math.min(MAX_TEST_MODE_DATA_SIZE, getTestHarnessModeData().length) + 4;
+ writeDataBuffer(getTestHarnessModeDataOffset(), ByteBuffer.allocate(size));
+ }
+
+ private void writeInternal(byte[] data, long offset, int dataLength) {
+ checkArgument(data == null || data.length > 0, "data must be null or non-empty");
+ checkArgument(
+ data == null || data.length <= dataLength,
+ "data must not be longer than " + dataLength);
+
+ ByteBuffer dataBuffer = ByteBuffer.allocate(dataLength + 4);
+ dataBuffer.putInt(data == null ? 0 : data.length);
+ if (data != null) {
+ dataBuffer.put(data);
+ }
+ dataBuffer.flip();
+
+ writeDataBuffer(offset, dataBuffer);
+ }
+
+ private void writeDataBuffer(long offset, ByteBuffer dataBuffer) {
FileOutputStream outputStream;
try {
outputStream = new FileOutputStream(new File(mDataBlockFile));
@@ -610,25 +704,15 @@
Slog.e(TAG, "partition not available", e);
return;
}
-
- ByteBuffer data = ByteBuffer.allocate(FRP_CREDENTIAL_RESERVED_SIZE);
- data.putInt(handle == null ? 0 : handle.length);
- if (handle != null) {
- data.put(handle);
- }
- data.flip();
-
synchronized (mLock) {
if (!mIsWritable) {
IoUtils.closeQuietly(outputStream);
return;
}
-
try {
FileChannel channel = outputStream.getChannel();
-
- channel.position(getBlockDeviceSize() - 1 - FRP_CREDENTIAL_RESERVED_SIZE);
- channel.write(data);
+ channel.position(offset);
+ channel.write(dataBuffer);
outputStream.flush();
} catch (IOException e) {
Slog.e(TAG, "unable to access persistent partition", e);
@@ -641,8 +725,7 @@
}
}
- @Override
- public byte[] getFrpCredentialHandle() {
+ private byte[] readInternal(long offset, int maxLength) {
if (!enforceChecksumValidity()) {
throw new IllegalStateException("invalid checksum");
}
@@ -652,14 +735,14 @@
inputStream = new DataInputStream(
new FileInputStream(new File(mDataBlockFile)));
} catch (FileNotFoundException e) {
- throw new IllegalStateException("frp partition not available");
+ throw new IllegalStateException("persistent partition not available");
}
try {
synchronized (mLock) {
- inputStream.skip(getBlockDeviceSize() - 1 - FRP_CREDENTIAL_RESERVED_SIZE);
+ inputStream.skip(offset);
int length = inputStream.readInt();
- if (length <= 0 || length > MAX_FRP_CREDENTIAL_HANDLE_SIZE) {
+ if (length <= 0 || length > maxLength) {
return null;
}
byte[] bytes = new byte[length];
@@ -667,7 +750,7 @@
return bytes;
}
} catch (IOException e) {
- throw new IllegalStateException("frp handle not readable", e);
+ throw new IllegalStateException("persistent partition not readable", e);
} finally {
IoUtils.closeQuietly(inputStream);
}
diff --git a/services/core/java/com/android/server/am/AppCompactor.java b/services/core/java/com/android/server/am/AppCompactor.java
index fe27c49..fd402cc 100644
--- a/services/core/java/com/android/server/am/AppCompactor.java
+++ b/services/core/java/com/android/server/am/AppCompactor.java
@@ -64,7 +64,7 @@
final private String COMPACT_ACTION_FILE = "file";
final private String COMPACT_ACTION_ANON = "anon";
- final private String COMPACT_ACTION_FULL = "full";
+ final private String COMPACT_ACTION_FULL = "all";
final private String compactActionSome;
final private String compactActionFull;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index d4b8eb2..944a95d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.UserIdInt;
-import android.content.ComponentName;
import android.view.inputmethod.InputMethodInfo;
import com.android.server.LocalServices;
@@ -42,11 +41,6 @@
public abstract void hideCurrentInputMethod();
/**
- * Switches to VR InputMethod defined in the packageName of {@param componentName}.
- */
- public abstract void startVrInputMethodNoCheck(ComponentName componentName);
-
- /**
* Returns the list of installed input methods for the specified user.
*
* @param userId The user ID to be queried.
@@ -76,10 +70,6 @@
}
@Override
- public void startVrInputMethodNoCheck(ComponentName componentName) {
- }
-
- @Override
public List<InputMethodInfo> getInputMethodListAsUser(int userId) {
return Collections.emptyList();
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 52074a7..eca371a 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -91,8 +91,6 @@
import android.os.UserManager;
import android.os.UserManagerInternal;
import android.provider.Settings;
-import android.service.vr.IVrManager;
-import android.service.vr.IVrStateCallbacks;
import android.text.TextUtils;
import android.text.style.SuggestionSpan;
import android.util.ArrayMap;
@@ -203,7 +201,6 @@
static final int MSG_CREATE_SESSION = 1050;
static final int MSG_START_INPUT = 2000;
- static final int MSG_START_VR_INPUT = 2010;
static final int MSG_UNBIND_CLIENT = 3000;
static final int MSG_BIND_CLIENT = 3010;
@@ -381,28 +378,6 @@
}
}
- /**
- * VR state callback.
- * Listens for when VR mode finishes.
- */
- private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
- @Override
- public void onVrStateChanged(boolean enabled) {
- if (!enabled) {
- restoreNonVrImeFromSettingsNoCheck();
- }
- }
- };
-
- private void restoreNonVrImeFromSettingsNoCheck() {
- // switch back to non-VR InputMethod from settings.
- synchronized (mMethodMap) {
- if (!mIsVrImeStarted) return;
- mIsVrImeStarted = false;
- updateFromSettingsLocked(false);
- }
- }
-
private static final class ClientDeathRecipient implements IBinder.DeathRecipient {
private final InputMethodManagerService mImms;
private final IInputMethodClient mClient;
@@ -597,9 +572,6 @@
final ImeDisplayValidator mImeDisplayValidator;
- /** True if VR IME started by {@link #startVrInputMethodNoCheck}. */
- boolean mIsVrImeStarted;
-
/**
* If non-null, this is the input method service we are currently connected
* to.
@@ -977,31 +949,6 @@
}
/**
- * Start a VR InputMethod that matches IME with package name of {@param component}.
- * Note: This method is called from {@link android.app.VrManager}.
- */
- private void startVrInputMethodNoCheck(@Nullable ComponentName component) {
- if (component == null) {
- // clear the current VR-only IME (if any) and restore normal IME.
- restoreNonVrImeFromSettingsNoCheck();
- return;
- }
-
- synchronized (mMethodMap) {
- String packageName = component.getPackageName();
- for (InputMethodInfo info : mMethodList) {
- if (TextUtils.equals(info.getPackageName(), packageName) && info.isVrOnly()) {
- // set this is as current inputMethod without updating settings.
- setInputMethodEnabledLocked(info.getId(), true);
- setInputMethodLocked(info.getId(), NOT_A_SUBTYPE_ID);
- mIsVrImeStarted = true;
- break;
- }
- }
- }
- }
-
- /**
* Handles {@link Intent#ACTION_LOCALE_CHANGED}.
*
* <p>Note: For historical reasons, {@link Intent#ACTION_LOCALE_CHANGED} has been sent to all
@@ -1452,15 +1399,6 @@
AdditionalSubtypeUtils.load(mAdditionalSubtypeMap, userId);
mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
mSettings, context);
- // Register VR-state listener.
- IVrManager vrManager = (IVrManager) ServiceManager.getService(Context.VR_SERVICE);
- if (vrManager != null) {
- try {
- vrManager.registerListener(mVrStateCallbacks);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to register VR mode state listener.");
- }
- }
}
private void resetDefaultImeLocked(Context context) {
@@ -1688,25 +1626,7 @@
}
final long ident = Binder.clearCallingIdentity();
try {
- return getInputMethodListLocked(false /* isVrOnly */, resolvedUserIds[0]);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- }
-
- @Override
- public List<InputMethodInfo> getVrInputMethodList() {
- final int callingUserId = UserHandle.getCallingUserId();
- synchronized (mMethodMap) {
- final int[] resolvedUserIds = InputMethodUtils.resolveUserId(callingUserId,
- mSettings.getCurrentUserId(), null);
- if (resolvedUserIds.length != 1) {
- return Collections.emptyList();
- }
- final long ident = Binder.clearCallingIdentity();
- try {
- return getInputMethodListLocked(true /* isVrOnly */, resolvedUserIds[0]);
+ return getInputMethodListLocked(resolvedUserIds[0]);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -1732,8 +1652,7 @@
}
@GuardedBy("mMethodMap")
- private List<InputMethodInfo> getInputMethodListLocked(boolean isVrOnly,
- @UserIdInt int userId) {
+ private List<InputMethodInfo> getInputMethodListLocked(@UserIdInt int userId) {
final ArrayList<InputMethodInfo> methodList;
if (userId == mSettings.getCurrentUserId()) {
// Create a copy.
@@ -1747,7 +1666,6 @@
queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
methodList);
}
- methodList.removeIf(imi -> imi.isVrOnly() != isVrOnly);
return methodList;
}
@@ -2021,8 +1939,8 @@
}
// Compute the final shown display ID with validated cs.selfReportedDisplayId for this
// session & other conditions.
- final int displayIdToShowIme = computeImeDisplayIdForTarget(
- cs.selfReportedDisplayId, mIsVrImeStarted, mImeDisplayValidator);
+ final int displayIdToShowIme = computeImeDisplayIdForTarget(cs.selfReportedDisplayId,
+ mImeDisplayValidator);
if (mCurClient != cs) {
// Was the keyguard locked when switching over to the new client?
@@ -2131,17 +2049,11 @@
* Find the display where the IME should be shown.
*
* @param displayId the ID of the display where the IME client target is.
- * @param isVrImeStarted {@code true} if VR IME started, {@code false} otherwise.
* @param checker instance of {@link ImeDisplayValidator} which is used for
* checking display config to adjust the final target display.
* @return The ID of the display where the IME should be shown.
*/
- static int computeImeDisplayIdForTarget(int displayId, boolean isVrImeStarted,
- @NonNull ImeDisplayValidator checker) {
- // For VR IME, we always show in default display.
- if (isVrImeStarted) {
- return DEFAULT_DISPLAY;
- }
+ static int computeImeDisplayIdForTarget(int displayId, @NonNull ImeDisplayValidator checker) {
if (displayId == DEFAULT_DISPLAY || displayId == INVALID_DISPLAY) {
// We always assume that the default display id suitable to show the IME window.
return DEFAULT_DISPLAY;
@@ -3627,9 +3539,6 @@
case MSG_SET_INTERACTIVE:
handleSetInteractive(msg.arg1 != 0);
return true;
- case MSG_START_VR_INPUT:
- startVrInputMethodNoCheck((ComponentName) msg.obj);
- return true;
case MSG_REPORT_FULLSCREEN_MODE: {
final boolean fullscreen = msg.arg1 != 0;
final ClientState clientState = (ClientState)msg.obj;
@@ -3716,6 +3625,9 @@
try {
final InputMethodInfo imi = new InputMethodInfo(context, ri,
additionalSubtypeMap.get(imeId));
+ if (imi.isVrOnly()) {
+ continue; // Skip VR-only IME, which isn't supported for now.
+ }
methodList.add(imi);
methodMap.put(imi.getId(), imi);
if (DEBUG) {
@@ -4099,17 +4011,7 @@
private void setSelectedInputMethodAndSubtypeLocked(InputMethodInfo imi, int subtypeId,
boolean setSubtypeOnly) {
- // Updates to InputMethod are transient in VR mode. Its not included in history.
- final boolean isVrInput = imi != null && imi.isVrOnly();
- if (!isVrInput) {
- // Update the history of InputMethod and Subtype
- mSettings.saveCurrentInputMethodAndSubtypeToHistory(mCurMethodId, mCurrentSubtype);
- }
-
- if (isVrInput) {
- // Updates to InputMethod are transient in VR mode. Any changes to Settings are skipped.
- return;
- }
+ mSettings.saveCurrentInputMethodAndSubtypeToHistory(mCurMethodId, mCurrentSubtype);
// Set Subtype here
if (imi == null || subtypeId < 0) {
@@ -4225,7 +4127,7 @@
private List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) {
synchronized (mMethodMap) {
- return getInputMethodListLocked(false, userId);
+ return getInputMethodListLocked(userId);
}
}
@@ -4257,11 +4159,6 @@
}
@Override
- public void startVrInputMethodNoCheck(@Nullable ComponentName componentName) {
- mService.mHandler.obtainMessage(MSG_START_VR_INPUT, componentName).sendToTarget();
- }
-
- @Override
public List<InputMethodInfo> getInputMethodListAsUser(int userId) {
return mService.getInputMethodListAsUser(userId);
}
@@ -4648,7 +4545,7 @@
mSettings.getCurrentUserId(), shellCommand.getErrPrintWriter());
for (int userId : userIds) {
final List<InputMethodInfo> methods = all
- ? getInputMethodListLocked(false, userId)
+ ? getInputMethodListLocked(userId)
: getEnabledInputMethodListLocked(userId);
if (userIds.length > 1) {
pr.print("User #");
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 6f0c5e8..a31b3b4 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -159,11 +159,6 @@
}
@Override
- public void startVrInputMethodNoCheck(ComponentName componentName) {
- reportNotSupported();
- }
-
- @Override
public List<InputMethodInfo> getInputMethodListAsUser(
@UserIdInt int userId) {
return userIdToInputMethodInfoMapper.getAsList(userId);
@@ -1244,13 +1239,6 @@
@BinderThread
@Override
- public List<InputMethodInfo> getVrInputMethodList() {
- reportNotSupported();
- return Collections.emptyList();
- }
-
- @BinderThread
- @Override
public List<InputMethodInfo> getEnabledInputMethodList() {
return mInputMethodInfoMap.getAsList(UserHandle.getUserId(Binder.getCallingUid()));
}
diff --git a/services/core/java/com/android/server/testharness/TestHarnessModeService.java b/services/core/java/com/android/server/testharness/TestHarnessModeService.java
new file mode 100644
index 0000000..23c042a
--- /dev/null
+++ b/services/core/java/com/android/server/testharness/TestHarnessModeService.java
@@ -0,0 +1,328 @@
+/*
+ * 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.testharness;
+
+import android.annotation.Nullable;
+import android.app.KeyguardManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.UserInfo;
+import android.os.BatteryManager;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.ShellCommand;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.util.Slog;
+
+import com.android.server.LocalServices;
+import com.android.server.PersistentDataBlockManagerInternal;
+import com.android.server.SystemService;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.attribute.PosixFilePermission;
+import java.util.Set;
+
+/**
+ * Manages the Test Harness Mode service for setting up test harness mode on the device.
+ *
+ * <p>Test Harness Mode is a feature that allows the user to clean their device, retain ADB keys,
+ * and provision the device for Instrumentation testing. This means that all parts of the device
+ * that would otherwise interfere with testing (auto-syncing accounts, package verification,
+ * automatic updates, etc.) are all disabled by default but may be re-enabled by the user.
+ */
+public class TestHarnessModeService extends SystemService {
+ private static final String TAG = TestHarnessModeService.class.getSimpleName();
+ private static final String TEST_HARNESS_MODE_PROPERTY = "persist.sys.test_harness";
+
+ private PersistentDataBlockManagerInternal mPersistentDataBlockManagerInternal;
+
+ public TestHarnessModeService(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService("testharness", mService);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ switch (phase) {
+ case PHASE_SYSTEM_SERVICES_READY:
+ setUpTestHarnessMode();
+ break;
+ case PHASE_BOOT_COMPLETED:
+ disableAutoSync();
+ break;
+ }
+ super.onBootPhase(phase);
+ }
+
+ private void setUpTestHarnessMode() {
+ Slog.d(TAG, "Setting up test harness mode");
+ byte[] testHarnessModeData = getPersistentDataBlock().getTestHarnessModeData();
+ if (testHarnessModeData == null || testHarnessModeData.length == 0) {
+ // There's no data to apply, so leave it as-is.
+ return;
+ }
+ PersistentData persistentData = PersistentData.fromBytes(testHarnessModeData);
+
+ SystemProperties.set(TEST_HARNESS_MODE_PROPERTY, persistentData.mEnabled ? "1" : "0");
+ writeAdbKeysFile(persistentData);
+ // Clear out the data block so that we don't revert the ADB keys on every boot.
+ getPersistentDataBlock().clearTestHarnessModeData();
+
+ ContentResolver cr = getContext().getContentResolver();
+ if (Settings.Global.getInt(cr, Settings.Global.ADB_ENABLED, 0) == 0) {
+ // Enable ADB
+ Settings.Global.putInt(cr, Settings.Global.ADB_ENABLED, 1);
+ } else {
+ // ADB is already enabled, we should restart the service so it picks up the new keys
+ android.os.SystemService.restart("adbd");
+ }
+
+ Settings.Global.putInt(cr, Settings.Global.PACKAGE_VERIFIER_ENABLE, 0);
+ Settings.Global.putInt(
+ cr,
+ Settings.Global.STAY_ON_WHILE_PLUGGED_IN,
+ BatteryManager.BATTERY_PLUGGED_ANY);
+ Settings.Global.putInt(cr, Settings.Global.OTA_DISABLE_AUTOMATIC_UPDATE, 1);
+ Settings.Global.putInt(cr, Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1);
+
+ setDeviceProvisioned();
+ }
+
+ private void disableAutoSync() {
+ UserInfo primaryUser = UserManager.get(getContext()).getPrimaryUser();
+ ContentResolver
+ .setMasterSyncAutomaticallyAsUser(false, primaryUser.getUserHandle().getIdentifier());
+ }
+
+ private void writeAdbKeysFile(PersistentData persistentData) {
+ Path adbKeys = Paths.get("/data/misc/adb/adb_keys");
+ try {
+ OutputStream fileOutputStream = Files.newOutputStream(adbKeys);
+ fileOutputStream.write(persistentData.mAdbKeys);
+ fileOutputStream.close();
+
+ Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(adbKeys);
+ permissions.add(PosixFilePermission.GROUP_READ);
+ Files.setPosixFilePermissions(adbKeys, permissions);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to set up adb keys", e);
+ // Note: if a device enters this block, it will remain UNAUTHORIZED in ADB, but all
+ // other settings will be set up.
+ }
+ }
+
+ // Setting the device as provisioned skips the setup wizard.
+ private void setDeviceProvisioned() {
+ ContentResolver cr = getContext().getContentResolver();
+ Settings.Global.putInt(cr, Settings.Global.DEVICE_PROVISIONED, 1);
+ Settings.Secure.putIntForUser(
+ cr,
+ Settings.Secure.USER_SETUP_COMPLETE,
+ 1,
+ UserHandle.USER_CURRENT);
+ }
+
+ @Nullable
+ private PersistentDataBlockManagerInternal getPersistentDataBlock() {
+ if (mPersistentDataBlockManagerInternal == null) {
+ Slog.d(TAG, "Getting PersistentDataBlockManagerInternal from LocalServices");
+ mPersistentDataBlockManagerInternal =
+ LocalServices.getService(PersistentDataBlockManagerInternal.class);
+ }
+ return mPersistentDataBlockManagerInternal;
+ }
+
+ private final IBinder mService = new Binder() {
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
+ (new TestHarnessModeShellCommand())
+ .exec(this, in, out, err, args, callback, resultReceiver);
+ }
+ };
+
+ private class TestHarnessModeShellCommand extends ShellCommand {
+ @Override
+ public int onCommand(String cmd) {
+ switch (cmd) {
+ case "enable":
+ case "restore":
+ checkPermissions();
+ final long originalId = Binder.clearCallingIdentity();
+ try {
+ if (isDeviceSecure()) {
+ getErrPrintWriter().println(
+ "Test Harness Mode cannot be enabled if there is a lock "
+ + "screen");
+ return 2;
+ }
+ return handleEnable();
+ } finally {
+ Binder.restoreCallingIdentity(originalId);
+ }
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ }
+
+ private void checkPermissions() {
+ getContext().enforceCallingPermission(
+ android.Manifest.permission.ENABLE_TEST_HARNESS_MODE,
+ "You must hold android.permission.ENABLE_TEST_HARNESS_MODE "
+ + "to enable Test Harness Mode");
+ }
+
+ private boolean isDeviceSecure() {
+ UserInfo primaryUser = UserManager.get(getContext()).getPrimaryUser();
+ KeyguardManager keyguardManager = getContext().getSystemService(KeyguardManager.class);
+ return keyguardManager.isDeviceSecure(primaryUser.id);
+ }
+
+ private int handleEnable() {
+ Path adbKeys = Paths.get("/data/misc/adb/adb_keys");
+ if (!Files.exists(adbKeys)) {
+ // This should only be accessible on eng builds that haven't yet set up ADB keys
+ getErrPrintWriter()
+ .println("No ADB keys stored; not enabling test harness mode");
+ return 1;
+ }
+
+ try (InputStream inputStream = Files.newInputStream(adbKeys)) {
+ long size = Files.size(adbKeys);
+ byte[] adbKeysBytes = new byte[(int) size];
+ int numBytes = inputStream.read(adbKeysBytes);
+ if (numBytes != size) {
+ getErrPrintWriter().println("Failed to read all bytes of adb_keys");
+ return 1;
+ }
+ PersistentData persistentData = new PersistentData(true, adbKeysBytes);
+ getPersistentDataBlock().setTestHarnessModeData(persistentData.toBytes());
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to store ADB keys.", e);
+ getErrPrintWriter().println("Failed to enable Test Harness Mode");
+ return 1;
+ }
+
+ Intent i = new Intent(Intent.ACTION_FACTORY_RESET);
+ i.setPackage("android");
+ i.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ i.putExtra(Intent.EXTRA_REASON, TAG);
+ i.putExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, true);
+ getContext().sendBroadcastAsUser(i, UserHandle.SYSTEM);
+ return 0;
+ }
+
+ @Override
+ public void onHelp() {
+ PrintWriter pw = getOutPrintWriter();
+ pw.println("About:");
+ pw.println(" Test Harness Mode is a mode that the device can be placed in to prepare");
+ pw.println(" the device for running UI tests. The device is placed into this mode by");
+ pw.println(" first wiping all data from the device, preserving ADB keys.");
+ pw.println();
+ pw.println(" By default, the following settings are configured:");
+ pw.println(" * Package Verifier is disabled");
+ pw.println(" * Stay Awake While Charging is enabled");
+ pw.println(" * OTA Updates are disabled");
+ pw.println(" * Auto-Sync for accounts is disabled");
+ pw.println();
+ pw.println(" Other apps may configure themselves differently in Test Harness Mode by");
+ pw.println(" checking ActivityManager.isRunningInUserTestHarness()");
+ pw.println();
+ pw.println("Test Harness Mode commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println();
+ pw.println(" enable|restore");
+ pw.println(" Erase all data from this device and enable Test Harness Mode,");
+ pw.println(" preserving the stored ADB keys currently on the device and toggling");
+ pw.println(" settings in a way that are conducive to Instrumentation testing.");
+ }
+ }
+
+ /**
+ * The object that will serialize/deserialize the Test Harness Mode data to and from the
+ * persistent data block.
+ */
+ public static class PersistentData {
+ static final byte VERSION_1 = 1;
+
+ final int mVersion;
+ final boolean mEnabled;
+ final byte[] mAdbKeys;
+
+ PersistentData(boolean enabled, byte[] adbKeys) {
+ this(VERSION_1, enabled, adbKeys);
+ }
+
+ PersistentData(int version, boolean enabled, byte[] adbKeys) {
+ this.mVersion = version;
+ this.mEnabled = enabled;
+ this.mAdbKeys = adbKeys;
+ }
+
+ static PersistentData fromBytes(byte[] bytes) {
+ try {
+ DataInputStream is = new DataInputStream(new ByteArrayInputStream(bytes));
+ int version = is.readInt();
+ boolean enabled = is.readBoolean();
+ int adbKeysLength = is.readInt();
+ byte[] adbKeys = new byte[adbKeysLength];
+ is.readFully(adbKeys);
+ return new PersistentData(version, enabled, adbKeys);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ byte[] toBytes() {
+ try {
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(os);
+ dos.writeInt(VERSION_1);
+ dos.writeBoolean(mEnabled);
+ dos.writeInt(mAdbKeys.length);
+ dos.write(mAdbKeys);
+ dos.close();
+ return os.toByteArray();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index b3eafa4..45689ce 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -65,7 +65,6 @@
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
import com.android.server.SystemService;
-import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.utils.ManagedApplicationService;
import com.android.server.utils.ManagedApplicationService.BinderChecker;
import com.android.server.utils.ManagedApplicationService.LogEvent;
@@ -623,14 +622,6 @@
}
@Override
- public void setVrInputMethod(ComponentName componentName) {
- enforceCallerPermissionAnyOf(Manifest.permission.RESTRICTED_VR_ACCESS);
- InputMethodManagerInternal imm =
- LocalServices.getService(InputMethodManagerInternal.class);
- imm.startVrInputMethodNoCheck(componentName);
- }
-
- @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
index e817dd4..65d66f4 100644
--- a/services/core/java/com/android/server/wm/ActivityDisplay.java
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -558,26 +558,22 @@
}
/**
- * Pause all activities in either all of the stacks or just the back stacks. This is done before
- * resuming a new activity and to make sure that previously active activities are
- * paused in stacks that are no longer visible or in pinned windowing mode. This does not
- * pause activities in visible stacks, so if an activity is launched within the same stack/task,
- * then we should explicitly pause that stack's top activity.
+ * Pause all activities in either all of the stacks or just the back stacks.
* @param userLeaving Passed to pauseActivity() to indicate whether to call onUserLeaving().
* @param resuming The resuming activity.
* @param dontWait The resuming activity isn't going to wait for all activities to be paused
* before resuming.
- * @return {@code true} if any activity was paused as a result of this call.
+ * @return true if any activity was paused as a result of this call.
*/
boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming, boolean dontWait) {
boolean someActivityPaused = false;
for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = mStacks.get(stackNdx);
- final ActivityRecord resumedActivity = stack.getResumedActivity();
- if (resumedActivity != null
- && (!stack.shouldBeVisible(resuming) || !stack.isFocusable())) {
+ // TODO(b/111541062): Check if resumed activity on this display instead
+ if (!mRootActivityContainer.isTopDisplayFocusedStack(stack)
+ && stack.getResumedActivity() != null) {
if (DEBUG_STATES) Slog.d(TAG_STATES, "pauseBackStacks: stack=" + stack +
- " mResumedActivity=" + resumedActivity);
+ " mResumedActivity=" + stack.getResumedActivity());
someActivityPaused |= stack.startPausingLocked(userLeaving, false, resuming,
dontWait);
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 6213fa0..b8634d8 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1946,84 +1946,30 @@
try {
mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
WindowVisibilityItem.obtain(true /* showWindow */));
- makeActiveIfNeeded(null /* activeActivity*/);
+ if (shouldPauseWhenBecomingVisible()) {
+ // An activity must be in the {@link PAUSING} state for the system to validate
+ // the move to {@link PAUSED}.
+ setState(PAUSING, "makeVisibleIfNeeded");
+ mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+ PauseActivityItem.obtain(finishing, false /* userLeaving */,
+ configChangeFlags, false /* dontReport */));
+ }
} catch (Exception e) {
Slog.w(TAG, "Exception thrown sending visibility update: " + intent.getComponent(), e);
}
}
- /**
- * Make activity resumed or paused if needed.
- * @param activeActivity an activity that is resumed or just completed pause action.
- * We won't change the state of this activity.
- */
- boolean makeActiveIfNeeded(ActivityRecord activeActivity) {
- if (shouldResumeActivity(activeActivity)) {
- if (DEBUG_VISIBILITY) {
- Slog.v("TAG_VISIBILITY", "Resume visible activity, " + this);
- }
- return getActivityStack().resumeTopActivityUncheckedLocked(activeActivity /* prev */,
- null /* options */);
- } else if (shouldPauseActivity(activeActivity)) {
- if (DEBUG_VISIBILITY) {
- Slog.v("TAG_VISIBILITY", "Pause visible activity, " + this);
- }
- // An activity must be in the {@link PAUSING} state for the system to validate
- // the move to {@link PAUSED}.
- setState(PAUSING, "makeVisibleIfNeeded");
- try {
- mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
- PauseActivityItem.obtain(finishing, false /* userLeaving */,
- configChangeFlags, false /* dontReport */));
- } catch (Exception e) {
- Slog.w(TAG, "Exception thrown sending pause: " + intent.getComponent(), e);
- }
- }
- return false;
- }
-
- /**
- * Check if activity should be moved to PAUSED state. The activity:
- * - should be eligible to be made active (see {@link #shouldMakeActive(ActivityRecord)})
- * - should be non-focusable
- * - should not be currently pausing or paused
- * @param activeActivity the activity that is active or just completed pause action. We won't
- * resume if this activity is active.
- */
- private boolean shouldPauseActivity(ActivityRecord activeActivity) {
- return shouldMakeActive(activeActivity) && !isFocusable() && !isState(PAUSING, PAUSED);
- }
-
- /**
- * Check if activity should be moved to RESUMED state. The activity:
- * - should be eligible to be made active (see {@link #shouldMakeActive(ActivityRecord)})
- * - should be focusable
- * @param activeActivity the activity that is active or just completed pause action. We won't
- * resume if this activity is active.
- */
- private boolean shouldResumeActivity(ActivityRecord activeActivity) {
- return shouldMakeActive(activeActivity) && isFocusable() && !isState(RESUMED);
- }
-
- /**
- * Check if activity is eligible to be made active (resumed of paused). The activity:
- * - should be paused, stopped or stopping
- * - should not be the currently active one
- * - should be either the topmost in task, or right below the top activity that is finishing
- * If all of these conditions are not met at the same time, the activity cannot be made active.
- */
- private boolean shouldMakeActive(ActivityRecord activeActivity) {
- // If the activity is stopped, stopping, cycle to an active state. We avoid doing
+ /** Check if activity should be moved to PAUSED state when it becomes visible. */
+ private boolean shouldPauseWhenBecomingVisible() {
+ // If the activity is stopped or stopping, cycle to the paused state. We avoid doing
// this when there is an activity waiting to become translucent as the extra binder
// calls will lead to noticeable jank. A later call to
- // ActivityStack#ensureActivitiesVisibleLocked will bring the activity to a proper
- // active state.
- if (!isState(RESUMED, PAUSED, STOPPED, STOPPING)
- || getActivityStack().mTranslucentActivityWaiting != null) {
- return false;
- }
-
- if (this == activeActivity) {
+ // ActivityStack#ensureActivitiesVisibleLocked will bring the activity to the proper
+ // paused state. We also avoid doing this for the activity the stack supervisor
+ // considers the resumed activity, as normal means will bring the activity from STOPPED
+ // to RESUMED. Adding PAUSING in this scenario will lead to double lifecycles.
+ if (!isState(STOPPED, STOPPING) || getActivityStack().mTranslucentActivityWaiting != null
+ || isResumedActivityOnDisplay()) {
return false;
}
@@ -2033,14 +1979,14 @@
throw new IllegalStateException("Activity not found in its task");
}
if (positionInTask == task.mActivities.size() - 1) {
- // It's the topmost activity in the task - should become resumed now
+ // It's the topmost activity in the task - should become paused now
return true;
}
// Check if activity above is finishing now and this one becomes the topmost in task.
final ActivityRecord activityAbove = task.mActivities.get(positionInTask + 1);
if (activityAbove.finishing && results == null) {
- // We will only allow making active if activity above wasn't launched for result.
- // Otherwise it will cause this activity to resume before getting result.
+ // We will only allow pausing if activity above wasn't launched for result. Otherwise it
+ // will cause this activity to resume before getting result.
return true;
}
return false;
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 3aef8e1f..891c3da 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -357,11 +357,6 @@
*/
boolean mForceHidden = false;
- /**
- * Used to keep resumeTopActivityUncheckedLocked() from being entered recursively
- */
- boolean mInResumeTopActivity = false;
-
private boolean mUpdateBoundsDeferred;
private boolean mUpdateBoundsDeferredCalled;
private boolean mUpdateDisplayedBoundsDeferredCalled;
@@ -1737,7 +1732,6 @@
"Activity paused: token=" + token + ", timeout=" + timeout);
final ActivityRecord r = isInStackLocked(token);
-
if (r != null) {
mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
if (mPausingActivity == r) {
@@ -2094,7 +2088,8 @@
boolean aboveTop = top != null;
final boolean stackShouldBeVisible = shouldBeVisible(starting);
boolean behindFullscreenActivity = !stackShouldBeVisible;
- boolean resumeNextActivity = isFocusable() && isInStackLocked(starting) == null;
+ boolean resumeNextActivity = mRootActivityContainer.isTopDisplayFocusedStack(this)
+ && (isInStackLocked(starting) == null);
final boolean isTopNotPinnedStack =
isAttached() && getDisplay().isTopNotPinnedStack(this);
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
@@ -2155,10 +2150,6 @@
if (r.handleAlreadyVisible()) {
resumeNextActivity = false;
}
-
- if (notifyClients) {
- r.makeActiveIfNeeded(starting);
- }
} else {
r.makeVisibleIfNeeded(starting, notifyClients);
}
@@ -2336,7 +2327,7 @@
r.setVisible(true);
}
if (r != starting) {
- mStackSupervisor.startSpecificActivityLocked(r, andResume, true /* checkConfig */);
+ mStackSupervisor.startSpecificActivityLocked(r, andResume, false);
return true;
}
}
@@ -2514,7 +2505,7 @@
*/
@GuardedBy("mService")
boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
- if (mInResumeTopActivity) {
+ if (mStackSupervisor.inResumeTopActivity) {
// Don't even start recursing.
return false;
}
@@ -2522,7 +2513,7 @@
boolean result = false;
try {
// Protect against recursion.
- mInResumeTopActivity = true;
+ mStackSupervisor.inResumeTopActivity = true;
result = resumeTopActivityInnerLocked(prev, options);
// When resuming the top activity, it may be necessary to pause the top activity (for
@@ -2537,7 +2528,7 @@
checkReadyForSleep();
}
} finally {
- mInResumeTopActivity = false;
+ mStackSupervisor.inResumeTopActivity = false;
}
return result;
@@ -2570,7 +2561,7 @@
// Find the next top-most activity to resume in this stack that is not finishing and is
// focusable. If it is not focusable, we will fall into the case below to resume the
// top activity in the next focusable task.
- ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
+ final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
final boolean hasRunningActivity = next != null;
@@ -2658,12 +2649,6 @@
if (!mRootActivityContainer.allPausedActivitiesComplete()) {
if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) Slog.v(TAG_PAUSE,
"resumeTopActivityLocked: Skip resume: some activity pausing.");
-
- // Adding previous activity to the waiting visible list, or it would be stopped
- // before top activity being visible.
- if (prev != null && !next.nowVisible) {
- mStackSupervisor.mActivitiesWaitingForVisibleActivity.add(prev);
- }
return false;
}
@@ -2873,9 +2858,7 @@
// the screen based on the new activity order.
boolean notUpdated = true;
- // Activity should also be visible if set mLaunchTaskBehind to true (see
- // ActivityRecord#shouldBeVisibleIgnoringKeyguard()).
- if (shouldBeVisible(next)) {
+ if (isFocusedStackOnDisplay()) {
// We have special rotation behavior when here is some active activity that
// requests specific orientation or Keyguard is locked. Make sure all activity
// visibilities are set correctly as well as the transition is updated if needed
@@ -4104,12 +4087,6 @@
mStackSupervisor.mFinishingActivities.add(r);
r.resumeKeyDispatchingLocked();
mRootActivityContainer.resumeFocusedStacksTopActivities();
- // If activity was not paused at this point - explicitly pause it to start finishing
- // process. Finishing will be completed once it reports pause back.
- if (r.isState(RESUMED) && mPausingActivity != null) {
- startPausingLocked(false /* userLeaving */, false /* uiSleeping */, next /* resuming */,
- false /* dontWait */);
- }
return r;
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index a83ef34..3a288ca 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -327,6 +327,9 @@
*/
PowerManager.WakeLock mGoingToSleep;
+ /** Used to keep resumeTopActivityUncheckedLocked() from being entered recursively */
+ boolean inResumeTopActivity;
+
/**
* Temporary rect used during docked stack resize calculation so we don't need to create a new
* object each time.
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 43c1206..2807094 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -179,10 +179,7 @@
.setActivityOptions(options.toBundle())
.execute();
mLastHomeActivityStartRecord = tmpOutRecord[0];
- final ActivityDisplay display =
- mService.mRootActivityContainer.getActivityDisplay(displayId);
- final ActivityStack homeStack = display != null ? display.getHomeStack() : null;
- if (homeStack != null && homeStack.mInResumeTopActivity) {
+ if (mSupervisor.inResumeTopActivity) {
// If we are in resume section already, home activity will be initialized, but not
// resumed (to avoid recursive resume) and will stay that way until something pokes it
// again. We need to schedule another resume.
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 4e2dffc..d36e545 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1629,7 +1629,7 @@
// Also, we don't want to resume activities in a task that currently has an overlay
// as the starting activity just needs to be in the visible paused state until the
// over is removed.
- mTargetStack.ensureActivitiesVisibleLocked(mStartActivity, 0, !PRESERVE_WINDOWS);
+ mTargetStack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
// Go ahead and tell window manager to execute app transition for this activity
// since the app transition will not be triggered through the resume channel.
mTargetStack.getDisplay().mDisplayContent.executeAppTransition();
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 9361e7f..750c5ca 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -2404,7 +2404,7 @@
@Override
protected void reparentSurfaceControl(Transaction t, SurfaceControl newParent) {
if (!mSurfaceAnimator.hasLeash()) {
- t.reparent(mSurfaceControl, newParent.getHandle());
+ t.reparent(mSurfaceControl, newParent);
}
}
@@ -2450,7 +2450,7 @@
t.setWindowCrop(mAnimationBoundsLayer, mTmpRect);
// Reparent leash to animation bounds layer.
- t.reparent(leash, mAnimationBoundsLayer.getHandle());
+ t.reparent(leash, mAnimationBoundsLayer);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index ac1159a..8fefd35 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -4588,7 +4588,7 @@
* Reparents the given surface to mOverlayLayer.
*/
void reparentToOverlay(Transaction transaction, SurfaceControl surface) {
- transaction.reparent(surface, mOverlayLayer.getHandle());
+ transaction.reparent(surface, mOverlayLayer);
}
void applyMagnificationSpec(MagnificationSpec spec) {
@@ -4831,11 +4831,11 @@
* Re-parent the DisplayContent's top surfaces, {@link #mWindowingLayer} and
* {@link #mOverlayLayer} to the specified surfaceControl.
*
- * @param surfaceControlHandle The handle for the new SurfaceControl, where the DisplayContent's
+ * @param surfaceControlHandle The new SurfaceControl, where the DisplayContent's
* surfaces will be re-parented to.
*/
- void reparentDisplayContent(IBinder surfaceControlHandle) {
- mPendingTransaction.reparent(mWindowingLayer, surfaceControlHandle)
- .reparent(mOverlayLayer, surfaceControlHandle);
+ void reparentDisplayContent(SurfaceControl sc) {
+ mPendingTransaction.reparent(mWindowingLayer, sc)
+ .reparent(mOverlayLayer, sc);
}
}
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index c4a853d..9b72141 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -1108,41 +1108,28 @@
return false;
}
- boolean result = false;
if (targetStack != null && (targetStack.isTopStackOnDisplay()
|| getTopDisplayFocusedStack() == targetStack)) {
- result = targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
+ return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
}
+ // Resume all top activities in focused stacks on all displays.
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- boolean resumedOnDisplay = false;
final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- final ActivityRecord topRunningActivity = stack.topRunningActivityLocked();
- if (!stack.isFocusableAndVisible() || topRunningActivity == null) {
- continue;
- }
- if (topRunningActivity.isState(RESUMED)) {
- // Kick off any lingering app transitions form the MoveTaskToFront operation.
- stack.executeAppTransition(targetOptions);
- } else {
- resumedOnDisplay |= topRunningActivity.makeActiveIfNeeded(target);
- }
+ final ActivityStack focusedStack = display.getFocusedStack();
+ if (focusedStack == null) {
+ continue;
}
- if (!resumedOnDisplay) {
- // In cases when there are no valid activities (e.g. device just booted or launcher
- // crashed) it's possible that nothing was resumed on a display. Requesting resume
- // of top activity in focused stack explicitly will make sure that at least home
- // activity is started and resumed, and no recursion occurs.
- final ActivityStack focusedStack = display.getFocusedStack();
- if (focusedStack != null) {
- focusedStack.resumeTopActivityUncheckedLocked(target, targetOptions);
- }
+ final ActivityRecord r = focusedStack.topRunningActivityLocked();
+ if (r == null || !r.isState(RESUMED)) {
+ focusedStack.resumeTopActivityUncheckedLocked(null, null);
+ } else if (r.isState(RESUMED)) {
+ // Kick off any lingering app transitions form the MoveTaskToFront operation.
+ focusedStack.executeAppTransition(targetOptions);
}
}
- return result;
+ return false;
}
void applySleepTokens(boolean applyToStacks) {
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 9d9b48a..1a8a911 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -199,7 +199,7 @@
* @see #setLayer
*/
void reparent(Transaction t, SurfaceControl newParent) {
- t.reparent(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), newParent.getHandle());
+ t.reparent(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), newParent);
}
/**
@@ -228,8 +228,8 @@
// Cancel source animation, but don't let animation runner cancel the animation.
from.cancelAnimation(t, false /* restarting */, false /* forwardCancel */);
- t.reparent(surface, mLeash.getHandle());
- t.reparent(mLeash, parent.getHandle());
+ t.reparent(surface, mLeash);
+ t.reparent(mLeash, parent);
mAnimatable.onAnimationLeashCreated(t, mLeash);
mService.mAnimationTransferMap.put(mAnimation, this);
}
@@ -275,7 +275,7 @@
final boolean destroy = mLeash != null && surface != null && parent != null;
if (destroy) {
if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to original parent");
- t.reparent(surface, parent.getHandle());
+ t.reparent(surface, parent);
scheduleAnim = true;
}
mService.mAnimationTransferMap.remove(mAnimation);
@@ -308,7 +308,7 @@
if (!hidden) {
t.show(leash);
}
- t.reparent(surface, leash.getHandle());
+ t.reparent(surface, leash);
return leash;
}
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index 0529ed1..69dcaf4 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -698,14 +698,6 @@
return false;
}
- final boolean toTopOfStack = position == MAX_VALUE;
- if (toTopOfStack && toStack.getResumedActivity() != null
- && toStack.topRunningActivityLocked() != null) {
- // Pause the resumed activity on the target stack while re-parenting task on top of it.
- toStack.startPausingLocked(false /* userLeaving */, false /* uiSleeping */,
- null /* resuming */, false /* pauseImmediately */);
- }
-
final int toStackWindowingMode = toStack.getWindowingMode();
final ActivityRecord topActivity = getTopActivity();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c8c834f..c6679a9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7334,7 +7334,7 @@
}
@Override
- public void reparentDisplayContent(int displayId, IBinder surfaceControlHandle) {
+ public void reparentDisplayContent(int displayId, SurfaceControl sc) {
final Display display = mDisplayManager.getDisplay(displayId);
if (display == null) {
throw new IllegalArgumentException(
@@ -7351,7 +7351,7 @@
long token = Binder.clearCallingIdentity();
try {
DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
- displayContent.reparentDisplayContent(surfaceControlHandle);
+ displayContent.reparentDisplayContent(sc);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 98385c9..623990b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -136,6 +136,7 @@
import com.android.server.statusbar.StatusBarManagerService;
import com.android.server.storage.DeviceStorageMonitorService;
import com.android.server.telecom.TelecomLoaderService;
+import com.android.server.testharness.TestHarnessModeService;
import com.android.server.textclassifier.TextClassificationManagerService;
import com.android.server.textservices.TextServicesManagerService;
import com.android.server.trust.TrustManagerService;
@@ -1157,6 +1158,10 @@
traceBeginAndSlog("StartPersistentDataBlock");
mSystemServiceManager.startService(PersistentDataBlockService.class);
traceEnd();
+
+ traceBeginAndSlog("StartTestHarnessMode");
+ mSystemServiceManager.startService(TestHarnessModeService.class);
+ traceEnd();
}
if (hasPdb || OemLockService.isHalPresent()) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
index 5083110..d91ce39 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
@@ -308,6 +308,24 @@
assertZoomsImmediatelyOnSwipeFrom(STATE_SHORTCUT_TRIGGERED);
}
+ @Test
+ public void testMultiTap_outOfDistanceSlop_shouldInIdle() {
+ // All delay motion events should be sent, if multi-tap with out of distance slop.
+ // STATE_IDLE will check if tapCount() < 2.
+ allowEventDelegation();
+ assertStaysIn(STATE_IDLE, () -> {
+ tap();
+ tap(DEFAULT_X * 2, DEFAULT_Y * 2);
+ });
+ assertStaysIn(STATE_IDLE, () -> {
+ tap();
+ tap(DEFAULT_X * 2, DEFAULT_Y * 2);
+ tap();
+ tap(DEFAULT_X * 2, DEFAULT_Y * 2);
+ tap();
+ });
+ }
+
private void assertZoomsImmediatelyOnSwipeFrom(int state) {
goFromStateIdleTo(state);
swipeAndHold();
@@ -531,6 +549,11 @@
send(upEvent());
}
+ private void tap(float x, float y) {
+ send(downEvent(x, y));
+ send(upEvent(x, y));
+ }
+
private void swipe() {
swipeAndHold();
send(upEvent());
@@ -572,18 +595,26 @@
}
private MotionEvent downEvent() {
+ return downEvent(DEFAULT_X, DEFAULT_Y);
+ }
+
+ private MotionEvent downEvent(float x, float y) {
mLastDownTime = mClock.now();
return fromTouchscreen(MotionEvent.obtain(mLastDownTime, mLastDownTime,
- ACTION_DOWN, DEFAULT_X, DEFAULT_Y, 0));
+ ACTION_DOWN, x, y, 0));
}
private MotionEvent upEvent() {
- return upEvent(mLastDownTime);
+ return upEvent(DEFAULT_X, DEFAULT_Y, mLastDownTime);
}
- private MotionEvent upEvent(long downTime) {
+ private MotionEvent upEvent(float x, float y) {
+ return upEvent(x, y, mLastDownTime);
+ }
+
+ private MotionEvent upEvent(float x, float y, long downTime) {
return fromTouchscreen(MotionEvent.obtain(downTime, mClock.now(),
- MotionEvent.ACTION_UP, DEFAULT_X, DEFAULT_Y, 0));
+ MotionEvent.ACTION_UP, x, y, 0));
}
private MotionEvent pointerEvent(int action, float x, float y) {
diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java
index 0328621..8afc3d3 100644
--- a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java
@@ -22,7 +22,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -58,8 +57,7 @@
// Make sure that there is a short-circuit for DEFAULT_DISPLAY.
assertEquals(DEFAULT_DISPLAY,
InputMethodManagerService.computeImeDisplayIdForTarget(
- DEFAULT_DISPLAY, false /* isVrImeStarted */,
- sMustNotBeCalledChecker));
+ DEFAULT_DISPLAY, sMustNotBeCalledChecker));
}
@Test
@@ -67,17 +65,7 @@
// Make sure that there is a short-circuit for INVALID_DISPLAY.
assertEquals(DEFAULT_DISPLAY,
InputMethodManagerService.computeImeDisplayIdForTarget(
- INVALID_DISPLAY, false /* isVrImeStarted */,
- sMustNotBeCalledChecker));
- }
-
- @Test
- public void testComputeImeDisplayId_VrIme() {
- // Make sure that there is a short-circuit for VR IME.
- assertEquals(DEFAULT_DISPLAY,
- InputMethodManagerService.computeImeDisplayIdForTarget(
- SYSTEM_DECORATION_SUPPORT_DISPLAY_ID, true /* isVrImeStarted */,
- sMustNotBeCalledChecker));
+ INVALID_DISPLAY, sMustNotBeCalledChecker));
}
@Test
@@ -86,8 +74,7 @@
// Make sure IME displayId is DEFAULT_DISPLAY.
assertEquals(DEFAULT_DISPLAY,
InputMethodManagerService.computeImeDisplayIdForTarget(
- NO_SYSTEM_DECORATION_SUPPORT_DISPLAY_ID, false /* isVrImeStarted */,
- sChecker));
+ NO_SYSTEM_DECORATION_SUPPORT_DISPLAY_ID, sChecker));
}
@Test
@@ -96,7 +83,6 @@
// Make sure IME displayId is the same display.
assertEquals(SYSTEM_DECORATION_SUPPORT_DISPLAY_ID,
InputMethodManagerService.computeImeDisplayIdForTarget(
- SYSTEM_DECORATION_SUPPORT_DISPLAY_ID, false /* isVrImeStarted */,
- sChecker));
+ SYSTEM_DECORATION_SUPPORT_DISPLAY_ID, sChecker));
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 319ffed..8be63fc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -31,7 +31,6 @@
import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT;
import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING;
import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
-import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING;
@@ -76,9 +75,6 @@
mStack = (TestActivityStack) new StackBuilder(mRootActivityContainer).build();
mTask = mStack.getChildAt(0);
mActivity = mTask.getTopActivity();
-
- doReturn(false).when(mService).isBooting();
- doReturn(true).when(mService).isBooted();
}
@Test
@@ -121,23 +117,22 @@
mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped");
- // The activity is in the focused stack so it should be resumed.
+ // The activity is in the focused stack so it should not move to paused.
mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
- assertTrue(mActivity.isState(RESUMED));
+ assertTrue(mActivity.isState(STOPPED));
assertFalse(pauseFound.value);
- // Make the activity non focusable
- mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped");
- doReturn(false).when(mActivity).isFocusable();
+ // Clear focused stack
+ final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
+ when(display.getFocusedStack()).thenReturn(null);
- // If the activity is not focusable, it should move to paused.
+ // In the unfocused stack, the activity should move to paused.
mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
assertTrue(mActivity.isState(PAUSING));
assertTrue(pauseFound.value);
// Make sure that the state does not change for current non-stopping states.
mActivity.setState(INITIALIZING, "testPausingWhenVisibleFromStopped");
- doReturn(true).when(mActivity).isFocusable();
mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index ea8f33f..68df87e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -55,7 +55,6 @@
import android.hardware.display.DisplayManagerGlobal;
import android.os.Handler;
import android.os.Looper;
-import android.os.PowerManager;
import android.os.Process;
import android.os.UserHandle;
import android.service.voice.IVoiceInteractionSession;
@@ -426,7 +425,6 @@
doReturn(mock(IPackageManager.class)).when(this).getPackageManager();
// allow background activity starts by default
doReturn(true).when(this).isBackgroundActivityStartsEnabled();
- doNothing().when(this).updateCpuStats();
}
void setup(IntentFirewall intentFirewall, PendingIntentController intentController,
@@ -582,8 +580,6 @@
doNothing().when(this).acquireLaunchWakelock();
doReturn(mKeyguardController).when(this).getKeyguardController();
- mLaunchingActivity = mock(PowerManager.WakeLock.class);
-
initialize();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
index d0b9225..ea5ab7b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
@@ -70,9 +70,9 @@
mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
verify(mTransaction).reparent(eq(mToken.getSurfaceControl()),
- eq(mToken.mSurfaceAnimator.mLeash.getHandle()));
+ eq(mToken.mSurfaceAnimator.mLeash));
verify(mTransaction).reparent(eq(mToken.mSurfaceAnimator.mLeash),
- eq(mToken.mAnimationBoundsLayer.getHandle()));
+ eq(mToken.mAnimationBoundsLayer));
}
@Test
@@ -111,7 +111,7 @@
mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
verify(mTransaction).reparent(eq(mToken.getSurfaceControl()),
- eq(mToken.mSurfaceAnimator.mLeash.getHandle()));
+ eq(mToken.mSurfaceAnimator.mLeash));
assertThat(mToken.mAnimationBoundsLayer).isNull();
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
index ad80cd6..9b84215 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -90,7 +90,7 @@
final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
OnAnimationFinishedCallback.class);
assertAnimating(mAnimatable);
- verify(mTransaction).reparent(eq(mAnimatable.mSurface), eq(mAnimatable.mLeash.getHandle()));
+ verify(mTransaction).reparent(eq(mAnimatable.mSurface), eq(mAnimatable.mLeash));
verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture());
callbackCaptor.getValue().onAnimationFinished(mSpec);
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 6c4b1af8..0fe5e08 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -793,15 +793,17 @@
* <p>
* Apps must be prepared for this method to return {@code null}, indicating that there currently
* exists no user-chosen default {@code PhoneAccount}.
+ * <p>
+ * The default dialer has access to use this method.
*
* @return The user outgoing phone account selected by the user.
- * @hide
*/
- @UnsupportedAppUsage
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public PhoneAccountHandle getUserSelectedOutgoingPhoneAccount() {
try {
if (isServiceConnected()) {
- return getTelecomService().getUserSelectedOutgoingPhoneAccount();
+ return getTelecomService().getUserSelectedOutgoingPhoneAccount(
+ mContext.getOpPackageName());
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#getUserSelectedOutgoingPhoneAccount", e);
@@ -810,10 +812,14 @@
}
/**
- * Sets the user-chosen default for making outgoing phone calls.
+ * Sets the user-chosen default {@link PhoneAccountHandle} for making outgoing phone calls.
+ *
+ * @param accountHandle The {@link PhoneAccountHandle} which will be used by default for making
+ * outgoing voice calls.
* @hide
*/
- @UnsupportedAppUsage
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @SystemApi
public void setUserSelectedOutgoingPhoneAccount(PhoneAccountHandle accountHandle) {
try {
if (isServiceConnected()) {
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 954a709..e1d5c17 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -45,7 +45,7 @@
/**
* @see TelecomServiceImpl#getUserSelectedOutgoingPhoneAccount
*/
- PhoneAccountHandle getUserSelectedOutgoingPhoneAccount();
+ PhoneAccountHandle getUserSelectedOutgoingPhoneAccount(String callingPackage);
/**
* @see TelecomServiceImpl#setUserSelectedOutgoingPhoneAccount
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 0e5c71d..0fa1b41 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -129,6 +129,66 @@
"android.telephony.euicc.action.RESOLVE_ERROR";
/**
+ * Intent action sent by system apps (such as the Settings app) to the Telephony framework to
+ * enable or disable a subscription. Must be accompanied with {@link #EXTRA_SUBSCRIPTION_ID} and
+ * {@link #EXTRA_ENABLE_SUBSCRIPTION}.
+ *
+ * <p>Unlike {@link #switchToSubscription(int, PendingIntent)}, using this action allows the
+ * underlying eUICC service (i.e. the LPA app) to control the UI experience during this
+ * operation. The action is received by the Telephony framework, which in turn selects and
+ * launches an appropriate LPA activity to present UI to the user. For example, the activity may
+ * show a confirmation dialog, a progress dialog, or an error dialog when necessary.
+ *
+ * <p>The launched activity will immediately finish with
+ * {@link android.app.Activity#RESULT_CANCELED} if {@link #isEnabled} is false.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED =
+ "android.telephony.euicc.action.TOGGLE_SUBSCRIPTION_PRIVILEGED";
+
+ /**
+ * Intent action sent by system apps (such as the Settings app) to the Telephony framework to
+ * delete a subscription. Must be accompanied with {@link #EXTRA_SUBSCRIPTION_ID}.
+ *
+ * <p>Unlike {@link #deleteSubscription(int, PendingIntent)}, using this action allows the
+ * underlying eUICC service (i.e. the LPA app) to control the UI experience during this
+ * operation. The action is received by the Telephony framework, which in turn selects and
+ * launches an appropriate LPA activity to present UI to the user. For example, the activity may
+ * show a confirmation dialog, a progress dialog, or an error dialog when necessary.
+ *
+ * <p>The launched activity will immediately finish with
+ * {@link android.app.Activity#RESULT_CANCELED} if {@link #isEnabled} is false.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String ACTION_DELETE_SUBSCRIPTION_PRIVILEGED =
+ "android.telephony.euicc.action.DELETE_SUBSCRIPTION_PRIVILEGED";
+
+ /**
+ * Intent action sent by system apps (such as the Settings app) to the Telephony framework to
+ * rename a subscription. Must be accompanied with {@link #EXTRA_SUBSCRIPTION_ID} and
+ * {@link #EXTRA_SUBSCRIPTION_NICKNAME}.
+ *
+ * <p>Unlike {@link #updateSubscriptionNickname(int, String, PendingIntent)}, using this action
+ * allows the the underlying eUICC service (i.e. the LPA app) to control the UI experience
+ * during this operation. The action is received by the Telephony framework, which in turn
+ * selects and launches an appropriate LPA activity to present UI to the user. For example, the
+ * activity may show a confirmation dialog, a progress dialog, or an error dialog when
+ * necessary.
+ *
+ * <p>The launched activity will immediately finish with
+ * {@link android.app.Activity#RESULT_CANCELED} if {@link #isEnabled} is false.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String ACTION_RENAME_SUBSCRIPTION_PRIVILEGED =
+ "android.telephony.euicc.action.RENAME_SUBSCRIPTION_PRIVILEGED";
+
+ /**
* Result code for an operation indicating that the operation succeeded.
*/
public static final int EMBEDDED_SUBSCRIPTION_RESULT_OK = 0;
@@ -219,6 +279,37 @@
"android.telephony.euicc.extra.FORCE_PROVISION";
/**
+ * Key for an extra set on privileged actions {@link #ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED},
+ * {@link #ACTION_DELETE_SUBSCRIPTION_PRIVILEGED}, and
+ * {@link #ACTION_RENAME_SUBSCRIPTION_PRIVILEGED} providing the ID of the targeted subscription.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_SUBSCRIPTION_ID =
+ "android.telephony.euicc.extra.SUBSCRIPTION_ID";
+
+ /**
+ * Key for an extra set on {@link #ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED} providing a boolean
+ * value of whether to enable or disable the targeted subscription.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_ENABLE_SUBSCRIPTION =
+ "android.telephony.euicc.extra.ENABLE_SUBSCRIPTION";
+
+ /**
+ * Key for an extra set on {@link #ACTION_RENAME_SUBSCRIPTION_PRIVILEGED} providing a new
+ * nickname for the targeted subscription.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_SUBSCRIPTION_NICKNAME =
+ "android.telephony.euicc.extra.SUBSCRIPTION_NICKNAME";
+
+ /**
* Optional meta-data attribute for a carrier app providing an icon to use to represent the
* carrier. If not provided, the app's launcher icon will be used as a fallback.
*/