diff options
43 files changed, 846 insertions, 1426 deletions
diff --git a/api/system-current.txt b/api/system-current.txt index 30a0c850f865..ac8fc9c23cb8 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -523,11 +523,11 @@ package android.app.admin { method public android.content.ComponentName getDeviceOwnerComponentOnAnyUser(); method public java.lang.String getDeviceOwnerNameOnAnyUser(); method public java.lang.CharSequence getDeviceOwnerOrganizationName(); - method public int getDeviceOwnerUserId(); + method public android.os.UserHandle getDeviceOwnerUser(); method public java.util.List<java.lang.String> getPermittedAccessibilityServices(int); method public java.util.List<java.lang.String> getPermittedInputMethodsForCurrentUser(); method public android.content.ComponentName getProfileOwner() throws java.lang.IllegalArgumentException; - method public android.content.ComponentName getProfileOwnerAsUser(int); + method public android.content.ComponentName getProfileOwnerAsUser(android.os.UserHandle); method public java.lang.String getProfileOwnerNameAsUser(int) throws java.lang.IllegalArgumentException; method public int getUserProvisioningState(); method public boolean isDeviceManaged(); @@ -4132,7 +4132,6 @@ package android.os { field public static final android.os.UserHandle ALL; field public static final android.os.UserHandle CURRENT; field public static final android.os.UserHandle SYSTEM; - field public static final int USER_NULL = -10000; // 0xffffd8f0 } public class UserManager { diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index a1b8d864d4de..74fb4df112b8 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -5224,13 +5224,30 @@ public class DevicePolicyManager { } /** - * @return ID of the user who runs device owner, or {@link UserHandle#USER_NULL} if there's - * no device owner. + * @return Handle of the user who runs device owner, or {@code null} if there's no device owner. * * @hide */ @RequiresPermission(android.Manifest.permission.MANAGE_USERS) @SystemApi + public @Nullable UserHandle getDeviceOwnerUser() { + if (mService != null) { + try { + int userId = mService.getDeviceOwnerUserId(); + + if (userId != UserHandle.USER_NULL) { + return UserHandle.of(userId); + } + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + return null; + } + + /** + * @hide + */ public int getDeviceOwnerUserId() { if (mService != null) { try { @@ -5653,6 +5670,20 @@ public class DevicePolicyManager { @RequiresPermission(value = android.Manifest.permission.INTERACT_ACROSS_USERS, conditional = true) @SystemApi + public @Nullable ComponentName getProfileOwnerAsUser(@NonNull UserHandle user) { + if (mService != null) { + try { + return mService.getProfileOwnerAsUser(user.getIdentifier()); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + return null; + } + + /** + * @hide + */ public @Nullable ComponentName getProfileOwnerAsUser(final int userId) { if (mService != null) { try { @@ -5703,6 +5734,37 @@ public class DevicePolicyManager { } /** + * Returns whether the specified package can read the device identifiers. + * + * @param packageName The package name of the app to check for device identifier access. + * @return whether the package can read the device identifiers. + * + * @hide + */ + public boolean checkDeviceIdentifierAccess(String packageName) { + return checkDeviceIdentifierAccessAsUser(packageName, myUserId()); + } + + /** + * @hide + */ + @RequiresPermission(value = android.Manifest.permission.MANAGE_USERS, conditional = true) + public boolean checkDeviceIdentifierAccessAsUser(String packageName, int userId) { + throwIfParentInstance("checkDeviceIdentifierAccessAsUser"); + if (packageName == null) { + return false; + } + if (mService != null) { + try { + return mService.checkDeviceIdentifierAccess(packageName, userId); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + return false; + } + + /** * Called by a profile owner or device owner to set a default activity that the system selects * to handle intents that match the given {@link IntentFilter}. This activity will remain the * default intent handler even if the set of potential event handlers for the intent filter diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 35ea250828aa..5e454506e9eb 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -153,6 +153,8 @@ interface IDevicePolicyManager { void clearProfileOwner(in ComponentName who); boolean hasUserSetupCompleted(); + boolean checkDeviceIdentifierAccess(in String packageName, int userHandle); + void setDeviceOwnerLockScreenInfo(in ComponentName who, CharSequence deviceOwnerInfo); CharSequence getDeviceOwnerLockScreenInfo(); diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java index c1ca63666baf..fefb8d3fc139 100644 --- a/core/java/android/app/assist/AssistStructure.java +++ b/core/java/android/app/assist/AssistStructure.java @@ -74,6 +74,7 @@ public class AssistStructure implements Parcelable { ComponentName mActivityComponent; private boolean mIsHomeActivity; private int mFlags; + private int mAutofillFlags; final ArrayList<WindowNode> mWindowNodes = new ArrayList<>(); @@ -197,6 +198,7 @@ public class AssistStructure implements Parcelable { mWriteStructure = as.waitForReady(); ComponentName.writeToParcel(as.mActivityComponent, out); out.writeInt(as.mFlags); + out.writeInt(as.mAutofillFlags); out.writeLong(as.mAcquisitionStartTime); out.writeLong(as.mAcquisitionEndTime); mNumWindows = as.mWindowNodes.size(); @@ -352,6 +354,7 @@ public class AssistStructure implements Parcelable { fetchData(); mActivityComponent = ComponentName.readFromParcel(mCurParcel); mFlags = mCurParcel.readInt(); + mAutofillFlags = mCurParcel.readInt(); mAcquisitionStartTime = mCurParcel.readLong(); mAcquisitionEndTime = mCurParcel.readLong(); final int N = mCurParcel.readInt(); @@ -625,8 +628,6 @@ public class AssistStructure implements Parcelable { String mIdType; String mIdEntry; - // TODO(b/37567426): once we have more flags, it might be better to store the individual - // fields (viewId and childId) of the field. AutofillId mAutofillId; @View.AutofillType int mAutofillType = View.AUTOFILL_TYPE_NONE; @Nullable String[] mAutofillHints; @@ -669,11 +670,6 @@ public class AssistStructure implements Parcelable { static final int FLAGS_CONTEXT_CLICKABLE = 0x00004000; static final int FLAGS_OPAQUE = 0x00008000; - // TODO(b/37567426): autofill data is made of many fields and ideally we should verify - // one-by-one to optimize what's sent over, but there isn't enough flag bits for that, we'd - // need to create a 'flags2' or 'autoFillFlags' field and add these flags there. - // So, to keep thinkg simpler for now, let's just use on flag for all of them... - static final int FLAGS_HAS_AUTOFILL_DATA = 0x80000000; static final int FLAGS_HAS_MATRIX = 0x40000000; static final int FLAGS_HAS_ALPHA = 0x20000000; static final int FLAGS_HAS_ELEVATION = 0x10000000; @@ -690,7 +686,20 @@ public class AssistStructure implements Parcelable { static final int FLAGS_HAS_LOCALE_LIST = 0x00010000; static final int FLAGS_ALL_CONTROL = 0xfff00000; + static final int AUTOFILL_FLAGS_HAS_AUTOFILL_VIEW_ID = 0x001; + static final int AUTOFILL_FLAGS_HAS_AUTOFILL_VIRTUAL_VIEW_ID = 0x002; + static final int AUTOFILL_FLAGS_HAS_AUTOFILL_VALUE = 0x004; + static final int AUTOFILL_FLAGS_HAS_AUTOFILL_TYPE = 0x008; + static final int AUTOFILL_FLAGS_HAS_AUTOFILL_HINTS = 0x010; + static final int AUTOFILL_FLAGS_HAS_AUTOFILL_OPTIONS = 0x020; + static final int AUTOFILL_FLAGS_HAS_HTML_INFO = 0x040; + static final int AUTOFILL_FLAGS_HAS_TEXT_ID_ENTRY = 0x080; + static final int AUTOFILL_FLAGS_HAS_MIN_TEXT_EMS = 0x100; + static final int AUTOFILL_FLAGS_HAS_MAX_TEXT_EMS = 0x200; + static final int AUTOFILL_FLAGS_HAS_MAX_TEXT_LENGTH = 0x400; + int mFlags; + int mAutofillFlags; String mClassName; CharSequence mContentDescription; @@ -714,6 +723,8 @@ public class AssistStructure implements Parcelable { mClassName = preader.readString(); mFlags = in.readInt(); final int flags = mFlags; + mAutofillFlags = in.readInt(); + final int autofillFlags = mAutofillFlags; if ((flags&FLAGS_HAS_ID) != 0) { mId = in.readInt(); if (mId != View.NO_ID) { @@ -725,22 +736,45 @@ public class AssistStructure implements Parcelable { } } - if ((flags&FLAGS_HAS_AUTOFILL_DATA) != 0) { + if (autofillFlags != 0) { mSanitized = in.readInt() == 1; - mAutofillId = in.readParcelable(null); - mAutofillType = in.readInt(); - mAutofillHints = in.readStringArray(); - mAutofillValue = in.readParcelable(null); - mAutofillOptions = in.readCharSequenceArray(); - final Parcelable p = in.readParcelable(null); - if (p instanceof HtmlInfo) { - mHtmlInfo = (HtmlInfo) p; - } - mMinEms = in.readInt(); - mMaxEms = in.readInt(); - mMaxLength = in.readInt(); - mTextIdEntry = preader.readString(); mImportantForAutofill = in.readInt(); + + if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_VIEW_ID) != 0) { + int autofillViewId = in.readInt(); + if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_VIRTUAL_VIEW_ID) != 0) { + mAutofillId = new AutofillId(autofillViewId, in.readInt()); + } else { + mAutofillId = new AutofillId(autofillViewId); + } + } + if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_TYPE) != 0) { + mAutofillType = in.readInt(); + } + if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_HINTS) != 0) { + mAutofillHints = in.readStringArray(); + } + if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_VALUE) != 0) { + mAutofillValue = in.readParcelable(null); + } + if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_OPTIONS) != 0) { + mAutofillOptions = in.readCharSequenceArray(); + } + if ((autofillFlags & AUTOFILL_FLAGS_HAS_HTML_INFO) != 0) { + mHtmlInfo = in.readParcelable(null); + } + if ((autofillFlags & AUTOFILL_FLAGS_HAS_MIN_TEXT_EMS) != 0) { + mMinEms = in.readInt(); + } + if ((autofillFlags & AUTOFILL_FLAGS_HAS_MAX_TEXT_EMS) != 0) { + mMaxEms = in.readInt(); + } + if ((autofillFlags & AUTOFILL_FLAGS_HAS_MAX_TEXT_LENGTH) != 0) { + mMaxLength = in.readInt(); + } + if ((autofillFlags & AUTOFILL_FLAGS_HAS_TEXT_ID_ENTRY) != 0) { + mTextIdEntry = preader.readString(); + } } if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) { mX = in.readInt(); @@ -808,13 +842,11 @@ public class AssistStructure implements Parcelable { boolean writeSensitive = true; int flags = mFlags & ~FLAGS_ALL_CONTROL; + int autofillFlags = 0; if (mId != View.NO_ID) { flags |= FLAGS_HAS_ID; } - if (mAutofillId != null) { - flags |= FLAGS_HAS_AUTOFILL_DATA; - } if ((mX&~0x7fff) != 0 || (mY&~0x7fff) != 0 || (mWidth&~0x7fff) != 0 | (mHeight&~0x7fff) != 0) { flags |= FLAGS_HAS_LARGE_COORDS; @@ -855,11 +887,44 @@ public class AssistStructure implements Parcelable { if (mChildren != null) { flags |= FLAGS_HAS_CHILDREN; } + if (mAutofillId != null) { + autofillFlags |= AUTOFILL_FLAGS_HAS_AUTOFILL_VIEW_ID; + if (mAutofillId.isVirtual()) { + autofillFlags |= AUTOFILL_FLAGS_HAS_AUTOFILL_VIRTUAL_VIEW_ID; + } + } + if (mAutofillValue != null) { + autofillFlags |= AUTOFILL_FLAGS_HAS_AUTOFILL_VALUE; + } + if (mAutofillType != View.AUTOFILL_TYPE_NONE) { + autofillFlags |= AUTOFILL_FLAGS_HAS_AUTOFILL_TYPE; + } + if (mAutofillHints != null) { + autofillFlags |= AUTOFILL_FLAGS_HAS_AUTOFILL_HINTS; + } + if (mAutofillOptions != null) { + autofillFlags |= AUTOFILL_FLAGS_HAS_AUTOFILL_OPTIONS; + } + if (mHtmlInfo instanceof Parcelable) { + autofillFlags |= AUTOFILL_FLAGS_HAS_HTML_INFO; + } + if (mMinEms > -1) { + autofillFlags |= AUTOFILL_FLAGS_HAS_MIN_TEXT_EMS; + } + if (mMaxEms > -1) { + autofillFlags |= AUTOFILL_FLAGS_HAS_MAX_TEXT_EMS; + } + if (mMaxLength > -1) { + autofillFlags |= AUTOFILL_FLAGS_HAS_MAX_TEXT_LENGTH; + } + if (mTextIdEntry != null) { + autofillFlags |= AUTOFILL_FLAGS_HAS_TEXT_ID_ENTRY; + } pwriter.writeString(mClassName); int writtenFlags = flags; - if ((flags&FLAGS_HAS_AUTOFILL_DATA) != 0 && (mSanitized || !sanitizeOnWrite)) { + if (autofillFlags != 0 && (mSanitized || !sanitizeOnWrite)) { // Remove 'checked' from sanitized autofill request. writtenFlags = flags & ~FLAGS_CHECKED; } @@ -872,6 +937,7 @@ public class AssistStructure implements Parcelable { } out.writeInt(writtenFlags); + out.writeInt(autofillFlags); if ((flags&FLAGS_HAS_ID) != 0) { out.writeInt(mId); if (mId != View.NO_ID) { @@ -883,32 +949,51 @@ public class AssistStructure implements Parcelable { } } - if ((flags&FLAGS_HAS_AUTOFILL_DATA) != 0) { - writeSensitive = mSanitized || !sanitizeOnWrite; + if (autofillFlags != 0) { out.writeInt(mSanitized ? 1 : 0); - out.writeParcelable(mAutofillId, 0); - out.writeInt(mAutofillType); - out.writeStringArray(mAutofillHints); - final AutofillValue sanitizedValue; - if (writeSensitive) { - sanitizedValue = mAutofillValue; - } else if (mAutofillOverlay != null && mAutofillOverlay.value != null) { - sanitizedValue = mAutofillOverlay.value; - } else { - sanitizedValue = null; + out.writeInt(mImportantForAutofill); + writeSensitive = mSanitized || !sanitizeOnWrite; + if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_VIEW_ID) != 0) { + out.writeInt(mAutofillId.getViewId()); + if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_VIRTUAL_VIEW_ID) != 0) { + out.writeInt(mAutofillId.getVirtualChildId()); + } + } + if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_TYPE) != 0) { + out.writeInt(mAutofillType); } - out.writeParcelable(sanitizedValue, 0); - out.writeCharSequenceArray(mAutofillOptions); - if (mHtmlInfo instanceof Parcelable) { + if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_HINTS) != 0) { + out.writeStringArray(mAutofillHints); + } + if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_VALUE) != 0) { + final AutofillValue sanitizedValue; + if (writeSensitive) { + sanitizedValue = mAutofillValue; + } else if (mAutofillOverlay != null && mAutofillOverlay.value != null) { + sanitizedValue = mAutofillOverlay.value; + } else { + sanitizedValue = null; + } + out.writeParcelable(sanitizedValue, 0); + } + if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_OPTIONS) != 0) { + out.writeCharSequenceArray(mAutofillOptions); + } + if ((autofillFlags & AUTOFILL_FLAGS_HAS_HTML_INFO) != 0) { out.writeParcelable((Parcelable) mHtmlInfo, 0); - } else { - out.writeParcelable(null, 0); } - out.writeInt(mMinEms); - out.writeInt(mMaxEms); - out.writeInt(mMaxLength); - pwriter.writeString(mTextIdEntry); - out.writeInt(mImportantForAutofill); + if ((autofillFlags & AUTOFILL_FLAGS_HAS_MIN_TEXT_EMS) != 0) { + out.writeInt(mMinEms); + } + if ((autofillFlags & AUTOFILL_FLAGS_HAS_MAX_TEXT_EMS) != 0) { + out.writeInt(mMaxEms); + } + if ((autofillFlags & AUTOFILL_FLAGS_HAS_MAX_TEXT_LENGTH) != 0) { + out.writeInt(mMaxLength); + } + if ((autofillFlags & AUTOFILL_FLAGS_HAS_TEXT_ID_ENTRY) != 0) { + pwriter.writeString(mTextIdEntry); + } } if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) { out.writeInt(mX); diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 8681893702b4..26c2033eac51 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -18,8 +18,10 @@ package android.os; import android.Manifest; import android.annotation.RequiresPermission; +import android.annotation.SuppressAutoDoc; import android.annotation.SystemApi; import android.annotation.TestApi; +import android.app.ActivityThread; import android.app.Application; import android.content.Context; import android.text.TextUtils; @@ -127,14 +129,21 @@ public class Build { * <a href="/training/articles/security-key-attestation.html">key attestation</a> to obtain * proof of the device's original identifiers. * + * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the + * device or profile owner. Profile owner access is deprecated and will be removed in a future + * release. + * * @return The serial number if specified. */ - @RequiresPermission(Manifest.permission.READ_PHONE_STATE) + @SuppressAutoDoc // No support for device / profile owner. + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static String getSerial() { IDeviceIdentifiersPolicyService service = IDeviceIdentifiersPolicyService.Stub .asInterface(ServiceManager.getService(Context.DEVICE_IDENTIFIERS_SERVICE)); try { - return service.getSerial(); + Application application = ActivityThread.currentApplication(); + String callingPackage = application != null ? application.getPackageName() : null; + return service.getSerialForPackage(callingPackage); } catch (RemoteException e) { e.rethrowFromSystemServer(); } diff --git a/core/java/android/os/IDeviceIdentifiersPolicyService.aidl b/core/java/android/os/IDeviceIdentifiersPolicyService.aidl index ac19f2bd5bbc..87d358f50e74 100644 --- a/core/java/android/os/IDeviceIdentifiersPolicyService.aidl +++ b/core/java/android/os/IDeviceIdentifiersPolicyService.aidl @@ -21,4 +21,5 @@ package android.os; */ interface IDeviceIdentifiersPolicyService { String getSerial(); + String getSerialForPackage(in String callingPackage); }
\ No newline at end of file diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java index 4fe2d58ff75f..f8feb7b4a693 100644 --- a/core/java/android/os/UserHandle.java +++ b/core/java/android/os/UserHandle.java @@ -61,7 +61,6 @@ public final class UserHandle implements Parcelable { public static final UserHandle CURRENT_OR_SELF = new UserHandle(USER_CURRENT_OR_SELF); /** @hide An undefined user id */ - @SystemApi public static final @UserIdInt int USER_NULL = -10000; /** diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 128217001b17..8123744281f4 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -268,7 +268,7 @@ public class UserManager { public static final String DISALLOW_INSTALL_UNKNOWN_SOURCES = "no_install_unknown_sources"; /** - * This restriction is a device-wide version of {@link DISALLOW_INSTALL_UNKNOWN_SOURCES}. + * This restriction is a device-wide version of {@link #DISALLOW_INSTALL_UNKNOWN_SOURCES}. * * Specifies if all users on the device are disallowed from enabling the * "Unknown Sources" setting, that allows installation of apps from unknown sources. diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java index 1f54ea53facc..63ff7b22f364 100644 --- a/core/java/android/os/storage/StorageManagerInternal.java +++ b/core/java/android/os/storage/StorageManagerInternal.java @@ -16,6 +16,9 @@ package android.os.storage; +import android.annotation.NonNull; +import android.annotation.Nullable; + /** * Mount service local interface. * @@ -81,18 +84,26 @@ public abstract class StorageManagerInternal { public abstract int getExternalStorageMountMode(int uid, String packageName); /** - * Mount external storage for the given package. + * Create storage sandbox for the given package. * * <p> This will involve calling into vold to setup appropriate bind mounts. * - * @param packageName The package for which external storage will be mounted. + * @param packageName The package for which the sandbox needs to be created. * @param appId The appId for the given package. * @param sharedUserId The sharedUserId for given package if it specified * {@code android:sharedUserId} in the manifest, otherwise {@code null} - * @param userId The userId in which the storage needs to be mounted. + * @param userId The userId in which the sandbox needs to be created. + */ + public abstract void prepareSandboxForApp(@NonNull String packageName, int appId, + @Nullable String sharedUserId, int userId); + + /** + * Delete storage sandbox for the given package. + * + * @param packageName The package for which the sandbox needs to be destroyed. + * @param userId The userId in which the sandbox needs to be destroyed. */ - public abstract void mountExternalStorageForApp(String packageName, int appId, - String sharedUserId, int userId); + public abstract void destroySandboxForApp(@NonNull String packageName, int userId); /** * @return Labels of storage volumes that are visible to the given userId. diff --git a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java index 4a58f885ff08..2989df83866c 100644 --- a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java +++ b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java @@ -16,6 +16,7 @@ package com.android.bandwidthtest; +import android.app.UiAutomation; import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkInfo.State; @@ -74,7 +75,13 @@ public class BandwidthTest extends InstrumentationTestCase { Log.v(LOG_TAG, "Initialized mConnectionUtil"); mUid = Process.myUid(); mTManager = (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE); - mDeviceId = mTManager.getDeviceId(); + final UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); + try { + uiAutomation.adoptShellPermissionIdentity(); + mDeviceId = mTManager.getDeviceId(); + } finally { + uiAutomation.dropShellPermissionIdentity(); + } } @Override diff --git a/core/tests/coretests/src/android/app/assist/AssistStructureTest.java b/core/tests/coretests/src/android/app/assist/AssistStructureTest.java index bdb3e089444f..fe51a39d3cd2 100644 --- a/core/tests/coretests/src/android/app/assist/AssistStructureTest.java +++ b/core/tests/coretests/src/android/app/assist/AssistStructureTest.java @@ -15,7 +15,9 @@ */ package android.app.assist; +import static android.view.View.AUTOFILL_TYPE_TEXT; import static android.view.View.IMPORTANT_FOR_AUTOFILL_AUTO; +import static android.view.View.IMPORTANT_FOR_AUTOFILL_YES; import static com.google.common.truth.Truth.assertThat; @@ -27,7 +29,9 @@ import android.os.SystemClock; import android.support.test.InstrumentationRegistry; import android.support.test.rule.ActivityTestRule; import android.support.test.runner.AndroidJUnit4; +import android.text.InputFilter; import android.util.Log; +import android.view.autofill.AutofillId; import android.widget.EditText; import android.widget.FrameLayout; import android.widget.LinearLayout; @@ -68,6 +72,16 @@ public class AssistStructureTest { // Cannot be much big because it could hang test due to blocking GC private static final int NUMBER_SMALL_VIEWS = 10_000; + // Autofill field constants + private static final AutofillId AUTOFILL_ID = new AutofillId(2); + private static final String AUTOFILL_HINTS = "hints"; + private static final int MIN_TEXT_EMS = 5; + private static final int MAX_TEXT_EMS = 17; + private static final int MAX_TEXT_LENGTH = 23; + + // ViewNodeBuilder structure for editing autofill fields + private AssistStructure.ViewNodeBuilder mBuilder; + private EmptyLayoutActivity mActivity; private final ActivityTestRule<EmptyLayoutActivity> mActivityTestRule = @@ -211,6 +225,12 @@ public class AssistStructureTest { private EditText newSmallView() { EditText view = new EditText(mContext); view.setText("I AM GROOT"); + view.setMinEms(MIN_TEXT_EMS); + view.setMaxEms(MAX_TEXT_EMS); + view.setAutofillId(AUTOFILL_ID); + view.setAutofillHints(AUTOFILL_HINTS); + view.setFilters(new InputFilter[] { new InputFilter.LengthFilter(MAX_TEXT_LENGTH) }); + view.setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES); return view; } @@ -220,6 +240,16 @@ public class AssistStructureTest { assertThat(view.getIdEntry()).isNull(); assertThat(view.getAutofillId()).isNotNull(); assertThat(view.getText().toString()).isEqualTo("I AM GROOT"); + + assertThat(view.getAutofillType()).isEqualTo(AUTOFILL_TYPE_TEXT); + + // fields controlled by mAutofillFlag + assertThat(view.getAutofillId().getViewId()).isEqualTo(2); + assertThat(view.getAutofillHints()[0]).isEqualTo(AUTOFILL_HINTS); + assertThat(view.getMinTextEms()).isEqualTo(MIN_TEXT_EMS); + assertThat(view.getMaxTextEms()).isEqualTo(MAX_TEXT_EMS); + assertThat(view.getMaxTextLength()).isEqualTo(MAX_TEXT_LENGTH); + assertThat(view.getImportantForAutofill()).isEqualTo(IMPORTANT_FOR_AUTOFILL_YES); } private EditText newBigView() { diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index b5d9952cf9b4..ed24543ed9bf 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -323,6 +323,7 @@ applications that come with the platform <permission name="android.permission.PACKAGE_USAGE_STATS" /> <permission name="android.permission.READ_FRAME_BUFFER"/> <permission name="android.permission.READ_LOWPAN_CREDENTIAL"/> + <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> <permission name="android.permission.REAL_GET_TASKS"/> <permission name="android.permission.REGISTER_CALL_PROVIDER"/> <permission name="android.permission.REGISTER_CONNECTION_MANAGER"/> diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 494e5135d5bb..e3ec45b20883 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -61,6 +61,7 @@ cc_defaults { shared_libs: [ "liblog", "libcutils", + "libstatslog", "libutils", "libEGL", "libGLESv2", diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp index f2d50cd42523..e7ae7675f0b8 100644 --- a/libs/hwui/JankTracker.cpp +++ b/libs/hwui/JankTracker.cpp @@ -18,6 +18,7 @@ #include <errno.h> #include <inttypes.h> +#include <statslog.h> #include <sys/mman.h> #include <algorithm> @@ -181,6 +182,7 @@ void JankTracker::finishFrame(const FrameInfo& frame) { ALOGI("%s", ss.str().c_str()); // Just so we have something that counts up, the value is largely irrelevant ATRACE_INT(ss.str().c_str(), ++sDaveyCount); + android::util::stats_write(android::util::DAVEY_OCCURRED, getuid(), ns2ms(totalDuration)); } } diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.h b/libs/hwui/pipeline/skia/RenderNodeDrawable.h index 6594bd22c0b1..d746978b0a61 100644 --- a/libs/hwui/pipeline/skia/RenderNodeDrawable.h +++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.h @@ -16,6 +16,8 @@ #pragma once +#include "SkiaUtils.h" + #include <SkCanvas.h> #include <SkDrawable.h> #include <SkMatrix.h> @@ -89,7 +91,7 @@ protected: virtual SkRect onGetBounds() override { // We don't want to enable a record time quick reject because the properties // of the RenderNode may be updated on subsequent frames. - return SkRect::MakeLargest(); + return SkRectMakeLargest(); } /** * This function draws into a canvas as forceDraw, but does nothing if the render node has a diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h index 3c48d3604864..26cfa908228c 100644 --- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h +++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h @@ -17,6 +17,7 @@ #pragma once #include "RenderNodeDrawable.h" +#include "SkiaUtils.h" #include <SkCanvas.h> #include <SkDrawable.h> @@ -41,7 +42,7 @@ public: explicit StartReorderBarrierDrawable(SkiaDisplayList* data); protected: - virtual SkRect onGetBounds() override { return SkRect::MakeLargest(); } + virtual SkRect onGetBounds() override { return SkRectMakeLargest(); } virtual void onDraw(SkCanvas* canvas) override; private: @@ -65,7 +66,7 @@ public: explicit EndReorderBarrierDrawable(StartReorderBarrierDrawable* startBarrier); protected: - virtual SkRect onGetBounds() override { return SkRect::MakeLargest(); } + virtual SkRect onGetBounds() override { return SkRectMakeLargest(); } virtual void onDraw(SkCanvas* canvas) override; private: diff --git a/libs/hwui/pipeline/skia/SkiaUtils.h b/libs/hwui/pipeline/skia/SkiaUtils.h new file mode 100644 index 000000000000..834446905216 --- /dev/null +++ b/libs/hwui/pipeline/skia/SkiaUtils.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <SkRect.h> + +namespace android { + +static inline SkRect SkRectMakeLargest() { + const SkScalar v = SK_ScalarMax; + return { -v, -v, v, v }; +}; + +} /* namespace android */ diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp index cdf31da37074..65b4e26ab62f 100644 --- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp +++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp @@ -24,6 +24,7 @@ #include "DamageAccumulator.h" #include "IContextFactory.h" #include "SkiaCanvas.h" +#include "pipeline/skia/SkiaUtils.h" #include "pipeline/skia/SkiaDisplayList.h" #include "pipeline/skia/SkiaOpenGLPipeline.h" #include "pipeline/skia/SkiaRecordingCanvas.h" @@ -41,7 +42,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrame) { redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver); }); LayerUpdateQueue layerUpdateQueue; - SkRect dirty = SkRect::MakeLargest(); + SkRect dirty = SkRectMakeLargest(); std::vector<sp<RenderNode>> renderNodes; renderNodes.push_back(redNode); bool opaque = true; @@ -62,7 +63,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, testOnPrepareTree) { }); LayerUpdateQueue layerUpdateQueue; - SkRect dirty = SkRect::MakeLargest(); + SkRect dirty = SkRectMakeLargest(); std::vector<sp<RenderNode>> renderNodes; renderNodes.push_back(redNode); bool opaque = true; @@ -97,7 +98,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) { bottomHalfGreenCanvas.drawRect(0, 1, 2, 2, greenPaint); }); LayerUpdateQueue layerUpdateQueue; - SkRect dirty = SkRect::MakeLargest(); + SkRect dirty = SkRectMakeLargest(); std::vector<sp<RenderNode>> renderNodes; renderNodes.push_back(halfGreenNode); android::uirenderer::Rect contentDrawBounds(0, 0, 2, 2); @@ -160,7 +161,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderLayer) { // attach both layers to the update queue LayerUpdateQueue layerUpdateQueue; - SkRect dirty = SkRect::MakeLargest(); + SkRect dirty = SkRectMakeLargest(); layerUpdateQueue.enqueueLayerWithDamage(redNode.get(), dirty); layerUpdateQueue.enqueueLayerWithDamage(blueNode.get(), SkRect::MakeWH(2, 1)); ASSERT_EQ(layerUpdateQueue.entries().size(), 2UL); diff --git a/media/java/android/media/MediaCrypto.java b/media/java/android/media/MediaCrypto.java index 474d8b9ef03c..889a5f7efbb2 100644 --- a/media/java/android/media/MediaCrypto.java +++ b/media/java/android/media/MediaCrypto.java @@ -56,13 +56,15 @@ public final class MediaCrypto { private static final native boolean isCryptoSchemeSupportedNative(@NonNull byte[] uuid); /** - * Instantiate a MediaCrypto object using opaque, crypto scheme specific - * data. + * Instantiate a MediaCrypto object and associate it with a MediaDrm session + * * @param uuid The UUID of the crypto scheme. - * @param initData Opaque initialization data specific to the crypto scheme. + * @param sessionId The MediaDrm sessionId to associate with this + * MediaCrypto session. The sessionId may be changed after the MediaCrypto + * is created using {@link #setMediaDrmSession} */ - public MediaCrypto(@NonNull UUID uuid, @NonNull byte[] initData) throws MediaCryptoException { - native_setup(getByteArrayFromUUID(uuid), initData); + public MediaCrypto(@NonNull UUID uuid, @NonNull byte[] sessionId) throws MediaCryptoException { + native_setup(getByteArrayFromUUID(uuid), sessionId); } /** diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java index 850be786cce1..6d122d77a999 100644 --- a/media/java/android/media/MediaDrm.java +++ b/media/java/android/media/MediaDrm.java @@ -1202,7 +1202,6 @@ public final class MediaDrm implements AutoCloseable { * The maximum security level supported by the device. This is the default * security level when a session is opened. */ - @SecurityLevel public static final int getMaxSecurityLevel() { return SECURITY_LEVEL_MAX; } diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java index d2989f09adb2..db6da8c590c3 100644 --- a/media/java/android/media/MediaPlayer2.java +++ b/media/java/android/media/MediaPlayer2.java @@ -32,9 +32,6 @@ import android.view.SurfaceHolder; import dalvik.system.CloseGuard; -import java.io.FileDescriptor; -import java.io.IOException; -import java.io.InputStream; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; @@ -232,8 +229,7 @@ import java.util.concurrent.Executor; * successful transition. Any other value will be an error. Call {@link #getState()} to * determine the current state. </p> */ -public abstract class MediaPlayer2 implements SubtitleController.Listener - , AutoCloseable +public abstract class MediaPlayer2 implements AutoCloseable , AudioRouting { private final CloseGuard mGuard = CloseGuard.get(); @@ -1081,16 +1077,6 @@ public abstract class MediaPlayer2 implements SubtitleController.Listener public abstract void reset(); /** - * Set up a timer for {@link #TimeProvider}. {@link #TimeProvider} will be - * notified when the presentation time reaches (becomes greater than or equal to) - * the value specified. - * - * @param mediaTimeUs presentation time to get timed event callback at - * @hide - */ - public void notifyAt(long mediaTimeUs) { } - - /** * Checks whether the MediaPlayer2 is looping or non-looping. * * @return true if the MediaPlayer2 is currently looping, false otherwise @@ -1237,95 +1223,6 @@ public abstract class MediaPlayer2 implements SubtitleController.Listener */ public static final String MEDIA_MIMETYPE_TEXT_CEA_708 = "text/cea-708"; - /** @hide */ - public void setSubtitleAnchor( - SubtitleController controller, - SubtitleController.Anchor anchor) { } - - /** @hide */ - @Override - public void onSubtitleTrackSelected(SubtitleTrack track) { } - - /** @hide */ - public void addSubtitleSource(InputStream is, MediaFormat format) { } - - /* TODO: Limit the total number of external timed text source to a reasonable number. - */ - /** - * Adds an external timed text source file. - * - * Currently supported format is SubRip with the file extension .srt, case insensitive. - * Note that a single external timed text source may contain multiple tracks in it. - * One can find the total number of available tracks using {@link #getTrackInfo()} to see what - * additional tracks become available after this method call. - * - * @param path The file path of external timed text source file. - * @param mimeType The mime type of the file. Must be one of the mime types listed above. - * @throws IOException if the file cannot be accessed or is corrupted. - * @throws IllegalArgumentException if the mimeType is not supported. - * @throws IllegalStateException if called in an invalid state. - * @hide - */ - public void addTimedTextSource(String path, String mimeType) throws IOException { } - - /** - * Adds an external timed text source file (Uri). - * - * Currently supported format is SubRip with the file extension .srt, case insensitive. - * Note that a single external timed text source may contain multiple tracks in it. - * One can find the total number of available tracks using {@link #getTrackInfo()} to see what - * additional tracks become available after this method call. - * - * @param context the Context to use when resolving the Uri - * @param uri the Content URI of the data you want to play - * @param mimeType The mime type of the file. Must be one of the mime types listed above. - * @throws IOException if the file cannot be accessed or is corrupted. - * @throws IllegalArgumentException if the mimeType is not supported. - * @throws IllegalStateException if called in an invalid state. - * @hide - */ - public void addTimedTextSource(Context context, Uri uri, String mimeType) throws IOException { } - - /** - * Adds an external timed text source file (FileDescriptor). - * - * It is the caller's responsibility to close the file descriptor. - * It is safe to do so as soon as this call returns. - * - * Currently supported format is SubRip. Note that a single external timed text source may - * contain multiple tracks in it. One can find the total number of available tracks - * using {@link #getTrackInfo()} to see what additional tracks become available - * after this method call. - * - * @param fd the FileDescriptor for the file you want to play - * @param mimeType The mime type of the file. Must be one of the mime types listed above. - * @throws IllegalArgumentException if the mimeType is not supported. - * @throws IllegalStateException if called in an invalid state. - * @hide - */ - public void addTimedTextSource(FileDescriptor fd, String mimeType) { } - - /** - * Adds an external timed text file (FileDescriptor). - * - * It is the caller's responsibility to close the file descriptor. - * It is safe to do so as soon as this call returns. - * - * Currently supported format is SubRip. Note that a single external timed text source may - * contain multiple tracks in it. One can find the total number of available tracks - * using {@link #getTrackInfo()} to see what additional tracks become available - * after this method call. - * - * @param fd the FileDescriptor for the file you want to play - * @param offset the offset into the file where the data to be played starts, in bytes - * @param length the length in bytes of the data to be played - * @param mime The mime type of the file. Must be one of the mime types listed above. - * @throws IllegalArgumentException if the mimeType is not supported. - * @throws IllegalStateException if called in an invalid state. - * @hide - */ - public abstract void addTimedTextSource(FileDescriptor fd, long offset, long length, String mime); - /** * Returns the index of the audio, video, or subtitle track currently selected for playback, * The return value is an index into the array returned by {@link #getTrackInfo()}, and can @@ -1393,11 +1290,6 @@ public abstract class MediaPlayer2 implements SubtitleController.Listener // This is an asynchronous call. public abstract void deselectTrack(int index); - /** @hide */ - public MediaTimeProvider getMediaTimeProvider() { - return null; - } - /** * Interface definition for callbacks to be invoked when the player has the corresponding * events. @@ -1679,12 +1571,6 @@ public abstract class MediaPlayer2 implements SubtitleController.Listener */ public static final int MEDIA_INFO_METADATA_UPDATE = 802; - /** A new set of external-only metadata is available. Used by - * JAVA framework to avoid triggering track scanning. - * @hide - */ - public static final int MEDIA_INFO_EXTERNAL_METADATA_UPDATE = 803; - /** Informs that audio is not playing. Note that playback of the video * is not interrupted. * @see EventCallback#onInfo @@ -1733,7 +1619,6 @@ public abstract class MediaPlayer2 implements SubtitleController.Listener MEDIA_INFO_BAD_INTERLEAVING, MEDIA_INFO_NOT_SEEKABLE, MEDIA_INFO_METADATA_UPDATE, - MEDIA_INFO_EXTERNAL_METADATA_UPDATE, MEDIA_INFO_AUDIO_NOT_PLAYING, MEDIA_INFO_VIDEO_NOT_PLAYING, MEDIA_INFO_TIMED_TEXT_ERROR, diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java index 2a03a899cce6..0e5cbe4e20b7 100644 --- a/media/java/android/media/MediaPlayer2Impl.java +++ b/media/java/android/media/MediaPlayer2Impl.java @@ -19,7 +19,6 @@ package android.media; import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.ActivityThread; import android.content.ContentProvider; import android.content.ContentResolver; import android.content.Context; @@ -29,8 +28,6 @@ import android.graphics.Rect; import android.media.MediaPlayer2Proto; import android.media.MediaPlayer2Proto.PlayerMessage; import android.media.MediaPlayer2Proto.Value; -import android.media.SubtitleController.Anchor; -import android.media.SubtitleTrack.RenderingWidget; import android.net.Uri; import android.os.Handler; import android.os.HandlerThread; @@ -38,23 +35,17 @@ import android.os.Looper; import android.os.Message; import android.os.PersistableBundle; import android.os.PowerManager; -import android.os.Process; import android.os.SystemProperties; import android.provider.Settings; -import android.system.ErrnoException; -import android.system.Os; -import android.system.OsConstants; import android.util.ArrayMap; import android.util.Log; import android.util.Pair; import android.view.Surface; import android.view.SurfaceHolder; -import android.widget.VideoView; import com.android.framework.protobuf.InvalidProtocolBufferException; import com.android.internal.annotations.GuardedBy; -import libcore.io.IoBridge; import java.io.ByteArrayOutputStream; import java.io.File; @@ -69,15 +60,12 @@ import java.net.URL; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Arrays; -import java.util.BitSet; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Scanner; import java.util.UUID; -import java.util.Vector; import java.util.concurrent.Executor; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.atomic.AtomicInteger; @@ -158,9 +146,6 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { Looper looper = mHandlerThread.getLooper(); mTaskHandler = new TaskHandler(this, looper); - mTimeProvider = new TimeProvider(this); - mOpenSubtitleSources = new Vector<InputStream>(); - /* Native setup requires a weak reference to our object. * It's easier to create it here than in C++. */ @@ -1506,24 +1491,6 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { */ @Override public void reset() { - mSelectedSubtitleTrackIndex = -1; - synchronized(mOpenSubtitleSources) { - for (final InputStream is: mOpenSubtitleSources) { - try { - is.close(); - } catch (IOException e) { - } - } - mOpenSubtitleSources.clear(); - } - if (mSubtitleController != null) { - mSubtitleController.reset(); - } - if (mTimeProvider != null) { - mTimeProvider.close(); - mTimeProvider = null; - } - synchronized (mEventCbLock) { mEventCallbackRecords.clear(); } @@ -1546,31 +1513,11 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { mTaskHandler.removeCallbacksAndMessages(null); } - synchronized (mIndexTrackPairs) { - mIndexTrackPairs.clear(); - mInbandTrackIndices.clear(); - }; - resetDrmState(); } private native void _reset(); - /** - * Set up a timer for {@link #TimeProvider}. {@link #TimeProvider} will be - * notified when the presentation time reaches (becomes greater than or equal to) - * the value specified. - * - * @param mediaTimeUs presentation time to get timed event callback at - * @hide - */ - @Override - public void notifyAt(long mediaTimeUs) { - _notifyAt(mediaTimeUs); - } - - private native void _notifyAt(long mediaTimeUs); - // Keep KEY_PARAMETER_* in sync with include/media/mediaplayer2.h private final static int KEY_PARAMETER_AUDIO_ATTRIBUTES = 1400; /** @@ -1784,16 +1731,6 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { } }; - // We would like domain specific classes with more informative names than the `first` and `second` - // in generic Pair, but we would also like to avoid creating new/trivial classes. As a compromise - // we document the meanings of `first` and `second` here: - // - // Pair.first - inband track index; non-null iff representing an inband track. - // Pair.second - a SubtitleTrack registered with mSubtitleController; non-null iff representing - // an inband subtitle track or any out-of-band track (subtitle or timedtext). - private Vector<Pair<Integer, SubtitleTrack>> mIndexTrackPairs = new Vector<>(); - private BitSet mInbandTrackIndices = new BitSet(); - /** * Returns a List of track information. * @@ -1805,21 +1742,7 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { @Override public List<TrackInfo> getTrackInfo() { TrackInfoImpl trackInfo[] = getInbandTrackInfoImpl(); - // add out-of-band tracks - synchronized (mIndexTrackPairs) { - TrackInfoImpl allTrackInfo[] = new TrackInfoImpl[mIndexTrackPairs.size()]; - for (int i = 0; i < allTrackInfo.length; i++) { - Pair<Integer, SubtitleTrack> p = mIndexTrackPairs.get(i); - if (p.first != null) { - // inband track - allTrackInfo[i] = trackInfo[p.first]; - } else { - SubtitleTrack track = p.second; - allTrackInfo[i] = new TrackInfoImpl(track.getTrackType(), track.getFormat()); - } - } - return Arrays.asList(allTrackInfo); - } + return Arrays.asList(trackInfo); } private TrackInfoImpl[] getInbandTrackInfoImpl() throws IllegalStateException { @@ -1852,419 +1775,6 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { return false; } - private SubtitleController mSubtitleController; - - /** @hide */ - @Override - public void setSubtitleAnchor( - SubtitleController controller, - SubtitleController.Anchor anchor) { - // TODO: create SubtitleController in MediaPlayer2 - mSubtitleController = controller; - mSubtitleController.setAnchor(anchor); - } - - /** - * The private version of setSubtitleAnchor is used internally to set mSubtitleController if - * necessary when clients don't provide their own SubtitleControllers using the public version - * {@link #setSubtitleAnchor(SubtitleController, Anchor)} (e.g. {@link VideoView} provides one). - */ - private synchronized void setSubtitleAnchor() { - if ((mSubtitleController == null) && (ActivityThread.currentApplication() != null)) { - final HandlerThread thread = new HandlerThread("SetSubtitleAnchorThread"); - thread.start(); - Handler handler = new Handler(thread.getLooper()); - handler.post(new Runnable() { - @Override - public void run() { - Context context = ActivityThread.currentApplication(); - mSubtitleController = new SubtitleController(context, mTimeProvider, MediaPlayer2Impl.this); - mSubtitleController.setAnchor(new Anchor() { - @Override - public void setSubtitleWidget(RenderingWidget subtitleWidget) { - } - - @Override - public Looper getSubtitleLooper() { - return Looper.getMainLooper(); - } - }); - thread.getLooper().quitSafely(); - } - }); - try { - thread.join(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - Log.w(TAG, "failed to join SetSubtitleAnchorThread"); - } - } - } - - private int mSelectedSubtitleTrackIndex = -1; - private Vector<InputStream> mOpenSubtitleSources; - - private EventCallback mSubtitleDataCallback = new EventCallback() { - @Override - public void onSubtitleData(MediaPlayer2 mp, DataSourceDesc dsd, SubtitleData data) { - int index = data.getTrackIndex(); - synchronized (mIndexTrackPairs) { - for (Pair<Integer, SubtitleTrack> p : mIndexTrackPairs) { - if (p.first != null && p.first == index && p.second != null) { - // inband subtitle track that owns data - SubtitleTrack track = p.second; - track.onData(data); - } - } - } - } - }; - - /** @hide */ - @Override - public void onSubtitleTrackSelected(SubtitleTrack track) { - if (mSelectedSubtitleTrackIndex >= 0) { - try { - selectOrDeselectInbandTrack(mSelectedSubtitleTrackIndex, false); - } catch (IllegalStateException e) { - } - mSelectedSubtitleTrackIndex = -1; - } - unregisterEventCallback(mSubtitleDataCallback); - if (track == null) { - return; - } - - synchronized (mIndexTrackPairs) { - for (Pair<Integer, SubtitleTrack> p : mIndexTrackPairs) { - if (p.first != null && p.second == track) { - // inband subtitle track that is selected - mSelectedSubtitleTrackIndex = p.first; - break; - } - } - } - - if (mSelectedSubtitleTrackIndex >= 0) { - try { - selectOrDeselectInbandTrack(mSelectedSubtitleTrackIndex, true); - } catch (IllegalStateException e) { - } - final Executor executor = (runnable) -> mTaskHandler.post(runnable); - registerEventCallback(executor, mSubtitleDataCallback); - } - // no need to select out-of-band tracks - } - - /** @hide */ - @Override - public void addSubtitleSource(InputStream is, MediaFormat format) - throws IllegalStateException - { - final InputStream fIs = is; - final MediaFormat fFormat = format; - - if (is != null) { - // Ensure all input streams are closed. It is also a handy - // way to implement timeouts in the future. - synchronized(mOpenSubtitleSources) { - mOpenSubtitleSources.add(is); - } - } else { - Log.w(TAG, "addSubtitleSource called with null InputStream"); - } - - getMediaTimeProvider(); - - // process each subtitle in its own thread - final HandlerThread thread = new HandlerThread("SubtitleReadThread", - Process.THREAD_PRIORITY_BACKGROUND + Process.THREAD_PRIORITY_MORE_FAVORABLE); - thread.start(); - Handler handler = new Handler(thread.getLooper()); - handler.post(new Runnable() { - private int addTrack() { - if (fIs == null || mSubtitleController == null) { - return MEDIA_INFO_UNSUPPORTED_SUBTITLE; - } - - SubtitleTrack track = mSubtitleController.addTrack(fFormat); - if (track == null) { - return MEDIA_INFO_UNSUPPORTED_SUBTITLE; - } - - // TODO: do the conversion in the subtitle track - Scanner scanner = new Scanner(fIs, "UTF-8"); - String contents = scanner.useDelimiter("\\A").next(); - synchronized(mOpenSubtitleSources) { - mOpenSubtitleSources.remove(fIs); - } - scanner.close(); - synchronized (mIndexTrackPairs) { - mIndexTrackPairs.add(Pair.<Integer, SubtitleTrack>create(null, track)); - } - Handler h = mTimeProvider.mEventHandler; - int what = TimeProvider.NOTIFY; - int arg1 = TimeProvider.NOTIFY_TRACK_DATA; - Pair<SubtitleTrack, byte[]> trackData = Pair.create(track, contents.getBytes()); - Message m = h.obtainMessage(what, arg1, 0, trackData); - h.sendMessage(m); - return MEDIA_INFO_EXTERNAL_METADATA_UPDATE; - } - - public void run() { - int res = addTrack(); - if (mTaskHandler != null) { - Message m = mTaskHandler.obtainMessage(MEDIA_INFO, res, 0, null); - mTaskHandler.sendMessage(m); - } - thread.getLooper().quitSafely(); - } - }); - } - - private void scanInternalSubtitleTracks() { - setSubtitleAnchor(); - - populateInbandTracks(); - - if (mSubtitleController != null) { - mSubtitleController.selectDefaultTrack(); - } - } - - private void populateInbandTracks() { - TrackInfoImpl[] tracks = getInbandTrackInfoImpl(); - synchronized (mIndexTrackPairs) { - for (int i = 0; i < tracks.length; i++) { - if (mInbandTrackIndices.get(i)) { - continue; - } else { - mInbandTrackIndices.set(i); - } - - // newly appeared inband track - if (tracks[i].getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE) { - SubtitleTrack track = mSubtitleController.addTrack( - tracks[i].getFormat()); - mIndexTrackPairs.add(Pair.create(i, track)); - } else { - mIndexTrackPairs.add(Pair.<Integer, SubtitleTrack>create(i, null)); - } - } - } - } - - /* TODO: Limit the total number of external timed text source to a reasonable number. - */ - /** - * Adds an external timed text source file. - * - * Currently supported format is SubRip with the file extension .srt, case insensitive. - * Note that a single external timed text source may contain multiple tracks in it. - * One can find the total number of available tracks using {@link #getTrackInfo()} to see what - * additional tracks become available after this method call. - * - * @param path The file path of external timed text source file. - * @param mimeType The mime type of the file. Must be one of the mime types listed above. - * @throws IOException if the file cannot be accessed or is corrupted. - * @throws IllegalArgumentException if the mimeType is not supported. - * @throws IllegalStateException if called in an invalid state. - * @hide - */ - @Override - public void addTimedTextSource(String path, String mimeType) - throws IOException { - if (!availableMimeTypeForExternalSource(mimeType)) { - final String msg = "Illegal mimeType for timed text source: " + mimeType; - throw new IllegalArgumentException(msg); - } - - File file = new File(path); - if (file.exists()) { - FileInputStream is = new FileInputStream(file); - FileDescriptor fd = is.getFD(); - addTimedTextSource(fd, mimeType); - is.close(); - } else { - // We do not support the case where the path is not a file. - throw new IOException(path); - } - } - - - /** - * Adds an external timed text source file (Uri). - * - * Currently supported format is SubRip with the file extension .srt, case insensitive. - * Note that a single external timed text source may contain multiple tracks in it. - * One can find the total number of available tracks using {@link #getTrackInfo()} to see what - * additional tracks become available after this method call. - * - * @param context the Context to use when resolving the Uri - * @param uri the Content URI of the data you want to play - * @param mimeType The mime type of the file. Must be one of the mime types listed above. - * @throws IOException if the file cannot be accessed or is corrupted. - * @throws IllegalArgumentException if the mimeType is not supported. - * @throws IllegalStateException if called in an invalid state. - * @hide - */ - @Override - public void addTimedTextSource(Context context, Uri uri, String mimeType) - throws IOException { - String scheme = uri.getScheme(); - if(scheme == null || scheme.equals("file")) { - addTimedTextSource(uri.getPath(), mimeType); - return; - } - - AssetFileDescriptor fd = null; - try { - ContentResolver resolver = context.getContentResolver(); - fd = resolver.openAssetFileDescriptor(uri, "r"); - if (fd == null) { - return; - } - addTimedTextSource(fd.getFileDescriptor(), mimeType); - return; - } catch (SecurityException ex) { - } catch (IOException ex) { - } finally { - if (fd != null) { - fd.close(); - } - } - } - - /** - * Adds an external timed text source file (FileDescriptor). - * - * It is the caller's responsibility to close the file descriptor. - * It is safe to do so as soon as this call returns. - * - * Currently supported format is SubRip. Note that a single external timed text source may - * contain multiple tracks in it. One can find the total number of available tracks - * using {@link #getTrackInfo()} to see what additional tracks become available - * after this method call. - * - * @param fd the FileDescriptor for the file you want to play - * @param mimeType The mime type of the file. Must be one of the mime types listed above. - * @throws IllegalArgumentException if the mimeType is not supported. - * @throws IllegalStateException if called in an invalid state. - * @hide - */ - @Override - public void addTimedTextSource(FileDescriptor fd, String mimeType) { - // intentionally less than LONG_MAX - addTimedTextSource(fd, 0, 0x7ffffffffffffffL, mimeType); - } - - /** - * Adds an external timed text file (FileDescriptor). - * - * It is the caller's responsibility to close the file descriptor. - * It is safe to do so as soon as this call returns. - * - * Currently supported format is SubRip. Note that a single external timed text source may - * contain multiple tracks in it. One can find the total number of available tracks - * using {@link #getTrackInfo()} to see what additional tracks become available - * after this method call. - * - * @param fd the FileDescriptor for the file you want to play - * @param offset the offset into the file where the data to be played starts, in bytes - * @param length the length in bytes of the data to be played - * @param mime The mime type of the file. Must be one of the mime types listed above. - * @throws IllegalArgumentException if the mimeType is not supported. - * @throws IllegalStateException if called in an invalid state. - * @hide - */ - @Override - public void addTimedTextSource(FileDescriptor fd, long offset, long length, String mime) { - if (!availableMimeTypeForExternalSource(mime)) { - throw new IllegalArgumentException("Illegal mimeType for timed text source: " + mime); - } - - final FileDescriptor dupedFd; - try { - dupedFd = Os.dup(fd); - } catch (ErrnoException ex) { - Log.e(TAG, ex.getMessage(), ex); - throw new RuntimeException(ex); - } - - final MediaFormat fFormat = new MediaFormat(); - fFormat.setString(MediaFormat.KEY_MIME, mime); - fFormat.setInteger(MediaFormat.KEY_IS_TIMED_TEXT, 1); - - // A MediaPlayer2 created by a VideoView should already have its mSubtitleController set. - if (mSubtitleController == null) { - setSubtitleAnchor(); - } - - if (!mSubtitleController.hasRendererFor(fFormat)) { - // test and add not atomic - Context context = ActivityThread.currentApplication(); - mSubtitleController.registerRenderer(new SRTRenderer(context, mTaskHandler)); - } - final SubtitleTrack track = mSubtitleController.addTrack(fFormat); - synchronized (mIndexTrackPairs) { - mIndexTrackPairs.add(Pair.<Integer, SubtitleTrack>create(null, track)); - } - - getMediaTimeProvider(); - - final long offset2 = offset; - final long length2 = length; - final HandlerThread thread = new HandlerThread( - "TimedTextReadThread", - Process.THREAD_PRIORITY_BACKGROUND + Process.THREAD_PRIORITY_MORE_FAVORABLE); - thread.start(); - Handler handler = new Handler(thread.getLooper()); - handler.post(new Runnable() { - private int addTrack() { - final ByteArrayOutputStream bos = new ByteArrayOutputStream(); - try { - Os.lseek(dupedFd, offset2, OsConstants.SEEK_SET); - byte[] buffer = new byte[4096]; - for (long total = 0; total < length2;) { - int bytesToRead = (int) Math.min(buffer.length, length2 - total); - int bytes = IoBridge.read(dupedFd, buffer, 0, bytesToRead); - if (bytes < 0) { - break; - } else { - bos.write(buffer, 0, bytes); - total += bytes; - } - } - Handler h = mTimeProvider.mEventHandler; - int what = TimeProvider.NOTIFY; - int arg1 = TimeProvider.NOTIFY_TRACK_DATA; - Pair<SubtitleTrack, byte[]> trackData = Pair.create(track, bos.toByteArray()); - Message m = h.obtainMessage(what, arg1, 0, trackData); - h.sendMessage(m); - return MEDIA_INFO_EXTERNAL_METADATA_UPDATE; - } catch (Exception e) { - Log.e(TAG, e.getMessage(), e); - return MEDIA_INFO_TIMED_TEXT_ERROR; - } finally { - try { - Os.close(dupedFd); - } catch (ErrnoException e) { - Log.e(TAG, e.getMessage(), e); - } - } - } - - public void run() { - int res = addTrack(); - if (mTaskHandler != null) { - Message m = mTaskHandler.obtainMessage(MEDIA_INFO, res, 0, null); - mTaskHandler.sendMessage(m); - } - thread.getLooper().quitSafely(); - } - }); - } - /** * Returns the index of the audio, video, or subtitle track currently selected for playback, * The return value is an index into the array returned by {@link #getTrackInfo()}, and can @@ -2284,22 +1794,6 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { */ @Override public int getSelectedTrack(int trackType) { - if (mSubtitleController != null - && (trackType == TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE - || trackType == TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT)) { - SubtitleTrack subtitleTrack = mSubtitleController.getSelectedTrack(); - if (subtitleTrack != null) { - synchronized (mIndexTrackPairs) { - for (int i = 0; i < mIndexTrackPairs.size(); i++) { - Pair<Integer, SubtitleTrack> p = mIndexTrackPairs.get(i); - if (p.second == subtitleTrack && subtitleTrack.getTrackType() == trackType) { - return i; - } - } - } - } - } - PlayerMessage request = PlayerMessage.newBuilder() .addValues(Value.newBuilder().setInt32Value(INVOKE_ID_GET_SELECTED_TRACK)) .addValues(Value.newBuilder().setInt32Value(trackType)) @@ -2308,16 +1802,7 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { if (response == null) { return -1; } - int inbandTrackIndex = response.getValues(0).getInt32Value(); - synchronized (mIndexTrackPairs) { - for (int i = 0; i < mIndexTrackPairs.size(); i++) { - Pair<Integer, SubtitleTrack> p = mIndexTrackPairs.get(i); - if (p.first != null && p.first == inbandTrackIndex) { - return i; - } - } - } - return -1; + return response.getValues(0).getInt32Value(); } /** @@ -2384,56 +1869,6 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { private void selectOrDeselectTrack(int index, boolean select) throws IllegalStateException { - // handle subtitle track through subtitle controller - populateInbandTracks(); - - Pair<Integer,SubtitleTrack> p = null; - try { - p = mIndexTrackPairs.get(index); - } catch (ArrayIndexOutOfBoundsException e) { - // ignore bad index - return; - } - - SubtitleTrack track = p.second; - if (track == null) { - // inband (de)select - selectOrDeselectInbandTrack(p.first, select); - return; - } - - if (mSubtitleController == null) { - return; - } - - if (!select) { - // out-of-band deselect - if (mSubtitleController.getSelectedTrack() == track) { - mSubtitleController.selectTrack(null); - } else { - Log.w(TAG, "trying to deselect track that was not selected"); - } - return; - } - - // out-of-band select - if (track.getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT) { - int ttIndex = getSelectedTrack(TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT); - synchronized (mIndexTrackPairs) { - if (ttIndex >= 0 && ttIndex < mIndexTrackPairs.size()) { - Pair<Integer,SubtitleTrack> p2 = mIndexTrackPairs.get(ttIndex); - if (p2.first != null && p2.second == null) { - // deselect inband counterpart - selectOrDeselectInbandTrack(p2.first, false); - } - } - } - } - mSubtitleController.selectTrack(track); - } - - private void selectOrDeselectInbandTrack(int index, boolean select) - throws IllegalStateException { PlayerMessage request = PlayerMessage.newBuilder() .addValues(Value.newBuilder().setInt32Value( select? INVOKE_ID_SELECT_TRACK: INVOKE_ID_DESELECT_TRACK)) @@ -2463,10 +1898,6 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { mHandlerThread.quitSafely(); mHandlerThread = null; } - if (mTimeProvider != null) { - mTimeProvider.close(); - mTimeProvider = null; - } // Modular DRM clean up mOnDrmConfigHelper = null; @@ -2503,17 +1934,6 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { private static final int MEDIA_DRM_INFO = 210; private static final int MEDIA_AUDIO_ROUTING_CHANGED = 10000; - private TimeProvider mTimeProvider; - - /** @hide */ - @Override - public MediaTimeProvider getMediaTimeProvider() { - if (mTimeProvider == null) { - mTimeProvider = new TimeProvider(this); - } - return mTimeProvider; - } - private class TaskHandler extends Handler { private MediaPlayer2Impl mMediaPlayer; @@ -2553,17 +1973,6 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { switch(msg.what) { case MEDIA_PREPARED: { - try { - scanInternalSubtitleTracks(); - } catch (RuntimeException e) { - // send error message instead of crashing; - // send error message instead of inlining a call to onError - // to avoid code duplication. - Message msg2 = obtainMessage( - MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null); - sendMessage(msg2); - } - if (dsd != null) { sendEvent(new EventNotifier() { @Override @@ -2659,21 +2068,13 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { } case MEDIA_STOPPED: - { - TimeProvider timeProvider = mTimeProvider; - if (timeProvider != null) { - timeProvider.onStopped(); - } - break; - } - case MEDIA_STARTED: case MEDIA_PAUSED: + case MEDIA_SKIPPED: + case MEDIA_NOTIFY_TIME: { - TimeProvider timeProvider = mTimeProvider; - if (timeProvider != null) { - timeProvider.onPaused(msg.what == MEDIA_PAUSED); - } + // Do nothing. The client should have enough information with + // {@link EventCallback#onMediaTimeDiscontinuity}. break; } @@ -2709,15 +2110,6 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { processPendingTask_l(); } } - } - // fall through - - case MEDIA_SKIPPED: - { - TimeProvider timeProvider = mTimeProvider; - if (timeProvider != null) { - timeProvider.onSeekComplete(mMediaPlayer); - } return; } @@ -2762,33 +2154,6 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { case MEDIA_INFO_VIDEO_TRACK_LAGGING: Log.i(TAG, "Info (" + msg.arg1 + "," + msg.arg2 + ")"); break; - - case MEDIA_INFO_METADATA_UPDATE: - try { - scanInternalSubtitleTracks(); - } catch (RuntimeException e) { - Message msg2 = obtainMessage( - MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, - null); - sendMessage(msg2); - } - // fall through - - case MEDIA_INFO_EXTERNAL_METADATA_UPDATE: - msg.arg1 = MEDIA_INFO_METADATA_UPDATE; - // update default track selection - if (mSubtitleController != null) { - mSubtitleController.selectDefaultTrack(); - } - break; - - case MEDIA_INFO_BUFFERING_START: - case MEDIA_INFO_BUFFERING_END: - TimeProvider timeProvider = mTimeProvider; - if (timeProvider != null) { - timeProvider.onBuffering(msg.arg1 == MEDIA_INFO_BUFFERING_START); - } - break; } sendEvent(new EventNotifier() { @@ -2809,15 +2174,6 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { return; } - case MEDIA_NOTIFY_TIME: - { - TimeProvider timeProvider = mTimeProvider; - if (timeProvider != null) { - timeProvider.onNotifyTime(); - } - return; - } - case MEDIA_TIMED_TEXT: { final TimedText text; @@ -4244,396 +3600,6 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { } } - /** @hide */ - static class TimeProvider implements MediaTimeProvider { - private static final String TAG = "MTP"; - private static final long MAX_NS_WITHOUT_POSITION_CHECK = 5000000000L; - private static final long MAX_EARLY_CALLBACK_US = 1000; - private static final long TIME_ADJUSTMENT_RATE = 2; /* meaning 1/2 */ - private long mLastTimeUs = 0; - private MediaPlayer2Impl mPlayer; - private boolean mPaused = true; - private boolean mStopped = true; - private boolean mBuffering; - private long mLastReportedTime; - // since we are expecting only a handful listeners per stream, there is - // no need for log(N) search performance - private MediaTimeProvider.OnMediaTimeListener mListeners[]; - private long mTimes[]; - private EventHandler mEventHandler; - private boolean mRefresh = false; - private boolean mPausing = false; - private boolean mSeeking = false; - private static final int NOTIFY = 1; - private static final int NOTIFY_TIME = 0; - private static final int NOTIFY_STOP = 2; - private static final int NOTIFY_SEEK = 3; - private static final int NOTIFY_TRACK_DATA = 4; - private HandlerThread mHandlerThread; - - /** @hide */ - public boolean DEBUG = false; - - public TimeProvider(MediaPlayer2Impl mp) { - mPlayer = mp; - try { - getCurrentTimeUs(true, false); - } catch (IllegalStateException e) { - // we assume starting position - mRefresh = true; - } - - Looper looper; - if ((looper = Looper.myLooper()) == null && - (looper = Looper.getMainLooper()) == null) { - // Create our own looper here in case MP was created without one - mHandlerThread = new HandlerThread("MediaPlayer2MTPEventThread", - Process.THREAD_PRIORITY_FOREGROUND); - mHandlerThread.start(); - looper = mHandlerThread.getLooper(); - } - mEventHandler = new EventHandler(looper); - - mListeners = new MediaTimeProvider.OnMediaTimeListener[0]; - mTimes = new long[0]; - mLastTimeUs = 0; - } - - private void scheduleNotification(int type, long delayUs) { - // ignore time notifications until seek is handled - if (mSeeking && type == NOTIFY_TIME) { - return; - } - - if (DEBUG) Log.v(TAG, "scheduleNotification " + type + " in " + delayUs); - mEventHandler.removeMessages(NOTIFY); - Message msg = mEventHandler.obtainMessage(NOTIFY, type, 0); - mEventHandler.sendMessageDelayed(msg, (int) (delayUs / 1000)); - } - - /** @hide */ - public void close() { - mEventHandler.removeMessages(NOTIFY); - if (mHandlerThread != null) { - mHandlerThread.quitSafely(); - mHandlerThread = null; - } - } - - /** @hide */ - protected void finalize() { - if (mHandlerThread != null) { - mHandlerThread.quitSafely(); - } - } - - /** @hide */ - public void onNotifyTime() { - synchronized (this) { - if (DEBUG) Log.d(TAG, "onNotifyTime: "); - scheduleNotification(NOTIFY_TIME, 0 /* delay */); - } - } - - /** @hide */ - public void onPaused(boolean paused) { - synchronized(this) { - if (DEBUG) Log.d(TAG, "onPaused: " + paused); - if (mStopped) { // handle as seek if we were stopped - mStopped = false; - mSeeking = true; - scheduleNotification(NOTIFY_SEEK, 0 /* delay */); - } else { - mPausing = paused; // special handling if player disappeared - mSeeking = false; - scheduleNotification(NOTIFY_TIME, 0 /* delay */); - } - } - } - - /** @hide */ - public void onBuffering(boolean buffering) { - synchronized (this) { - if (DEBUG) Log.d(TAG, "onBuffering: " + buffering); - mBuffering = buffering; - scheduleNotification(NOTIFY_TIME, 0 /* delay */); - } - } - - /** @hide */ - public void onStopped() { - synchronized(this) { - if (DEBUG) Log.d(TAG, "onStopped"); - mPaused = true; - mStopped = true; - mSeeking = false; - mBuffering = false; - scheduleNotification(NOTIFY_STOP, 0 /* delay */); - } - } - - /** @hide */ - public void onSeekComplete(MediaPlayer2Impl mp) { - synchronized(this) { - mStopped = false; - mSeeking = true; - scheduleNotification(NOTIFY_SEEK, 0 /* delay */); - } - } - - /** @hide */ - public void onNewPlayer() { - if (mRefresh) { - synchronized(this) { - mStopped = false; - mSeeking = true; - mBuffering = false; - scheduleNotification(NOTIFY_SEEK, 0 /* delay */); - } - } - } - - private synchronized void notifySeek() { - mSeeking = false; - try { - long timeUs = getCurrentTimeUs(true, false); - if (DEBUG) Log.d(TAG, "onSeekComplete at " + timeUs); - - for (MediaTimeProvider.OnMediaTimeListener listener: mListeners) { - if (listener == null) { - break; - } - listener.onSeek(timeUs); - } - } catch (IllegalStateException e) { - // we should not be there, but at least signal pause - if (DEBUG) Log.d(TAG, "onSeekComplete but no player"); - mPausing = true; // special handling if player disappeared - notifyTimedEvent(false /* refreshTime */); - } - } - - private synchronized void notifyTrackData(Pair<SubtitleTrack, byte[]> trackData) { - SubtitleTrack track = trackData.first; - byte[] data = trackData.second; - track.onData(data, true /* eos */, ~0 /* runID: keep forever */); - } - - private synchronized void notifyStop() { - for (MediaTimeProvider.OnMediaTimeListener listener: mListeners) { - if (listener == null) { - break; - } - listener.onStop(); - } - } - - private int registerListener(MediaTimeProvider.OnMediaTimeListener listener) { - int i = 0; - for (; i < mListeners.length; i++) { - if (mListeners[i] == listener || mListeners[i] == null) { - break; - } - } - - // new listener - if (i >= mListeners.length) { - MediaTimeProvider.OnMediaTimeListener[] newListeners = - new MediaTimeProvider.OnMediaTimeListener[i + 1]; - long[] newTimes = new long[i + 1]; - System.arraycopy(mListeners, 0, newListeners, 0, mListeners.length); - System.arraycopy(mTimes, 0, newTimes, 0, mTimes.length); - mListeners = newListeners; - mTimes = newTimes; - } - - if (mListeners[i] == null) { - mListeners[i] = listener; - mTimes[i] = MediaTimeProvider.NO_TIME; - } - return i; - } - - public void notifyAt( - long timeUs, MediaTimeProvider.OnMediaTimeListener listener) { - synchronized(this) { - if (DEBUG) Log.d(TAG, "notifyAt " + timeUs); - mTimes[registerListener(listener)] = timeUs; - scheduleNotification(NOTIFY_TIME, 0 /* delay */); - } - } - - public void scheduleUpdate(MediaTimeProvider.OnMediaTimeListener listener) { - synchronized(this) { - if (DEBUG) Log.d(TAG, "scheduleUpdate"); - int i = registerListener(listener); - - if (!mStopped) { - mTimes[i] = 0; - scheduleNotification(NOTIFY_TIME, 0 /* delay */); - } - } - } - - public void cancelNotifications( - MediaTimeProvider.OnMediaTimeListener listener) { - synchronized(this) { - int i = 0; - for (; i < mListeners.length; i++) { - if (mListeners[i] == listener) { - System.arraycopy(mListeners, i + 1, - mListeners, i, mListeners.length - i - 1); - System.arraycopy(mTimes, i + 1, - mTimes, i, mTimes.length - i - 1); - mListeners[mListeners.length - 1] = null; - mTimes[mTimes.length - 1] = NO_TIME; - break; - } else if (mListeners[i] == null) { - break; - } - } - - scheduleNotification(NOTIFY_TIME, 0 /* delay */); - } - } - - private synchronized void notifyTimedEvent(boolean refreshTime) { - // figure out next callback - long nowUs; - try { - nowUs = getCurrentTimeUs(refreshTime, true); - } catch (IllegalStateException e) { - // assume we paused until new player arrives - mRefresh = true; - mPausing = true; // this ensures that call succeeds - nowUs = getCurrentTimeUs(refreshTime, true); - } - long nextTimeUs = nowUs; - - if (mSeeking) { - // skip timed-event notifications until seek is complete - return; - } - - if (DEBUG) { - StringBuilder sb = new StringBuilder(); - sb.append("notifyTimedEvent(").append(mLastTimeUs).append(" -> ") - .append(nowUs).append(") from {"); - boolean first = true; - for (long time: mTimes) { - if (time == NO_TIME) { - continue; - } - if (!first) sb.append(", "); - sb.append(time); - first = false; - } - sb.append("}"); - Log.d(TAG, sb.toString()); - } - - Vector<MediaTimeProvider.OnMediaTimeListener> activatedListeners = - new Vector<MediaTimeProvider.OnMediaTimeListener>(); - for (int ix = 0; ix < mTimes.length; ix++) { - if (mListeners[ix] == null) { - break; - } - if (mTimes[ix] <= NO_TIME) { - // ignore, unless we were stopped - } else if (mTimes[ix] <= nowUs + MAX_EARLY_CALLBACK_US) { - activatedListeners.add(mListeners[ix]); - if (DEBUG) Log.d(TAG, "removed"); - mTimes[ix] = NO_TIME; - } else if (nextTimeUs == nowUs || mTimes[ix] < nextTimeUs) { - nextTimeUs = mTimes[ix]; - } - } - - if (nextTimeUs > nowUs && !mPaused) { - // schedule callback at nextTimeUs - if (DEBUG) Log.d(TAG, "scheduling for " + nextTimeUs + " and " + nowUs); - mPlayer.notifyAt(nextTimeUs); - } else { - mEventHandler.removeMessages(NOTIFY); - // no more callbacks - } - - for (MediaTimeProvider.OnMediaTimeListener listener: activatedListeners) { - listener.onTimedEvent(nowUs); - } - } - - public long getCurrentTimeUs(boolean refreshTime, boolean monotonic) - throws IllegalStateException { - synchronized (this) { - // we always refresh the time when the paused-state changes, because - // we expect to have received the pause-change event delayed. - if (mPaused && !refreshTime) { - return mLastReportedTime; - } - - try { - mLastTimeUs = mPlayer.getCurrentPosition() * 1000L; - mPaused = !mPlayer.isPlaying() || mBuffering; - if (DEBUG) Log.v(TAG, (mPaused ? "paused" : "playing") + " at " + mLastTimeUs); - } catch (IllegalStateException e) { - if (mPausing) { - // if we were pausing, get last estimated timestamp - mPausing = false; - if (!monotonic || mLastReportedTime < mLastTimeUs) { - mLastReportedTime = mLastTimeUs; - } - mPaused = true; - if (DEBUG) Log.d(TAG, "illegal state, but pausing: estimating at " + mLastReportedTime); - return mLastReportedTime; - } - // TODO get time when prepared - throw e; - } - if (monotonic && mLastTimeUs < mLastReportedTime) { - /* have to adjust time */ - if (mLastReportedTime - mLastTimeUs > 1000000) { - // schedule seeked event if time jumped significantly - // TODO: do this properly by introducing an exception - mStopped = false; - mSeeking = true; - scheduleNotification(NOTIFY_SEEK, 0 /* delay */); - } - } else { - mLastReportedTime = mLastTimeUs; - } - - return mLastReportedTime; - } - } - - private class EventHandler extends Handler { - public EventHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - if (msg.what == NOTIFY) { - switch (msg.arg1) { - case NOTIFY_TIME: - notifyTimedEvent(true /* refreshTime */); - break; - case NOTIFY_STOP: - notifyStop(); - break; - case NOTIFY_SEEK: - notifySeek(); - break; - case NOTIFY_TRACK_DATA: - notifyTrackData((Pair<SubtitleTrack, byte[]>)msg.obj); - break; - } - } - } - } - } - private abstract class Task implements Runnable { private final int mMediaCallType; private final boolean mNeedToWaitForEventToComplete; diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp index 693a3d0b23df..b52da362beff 100644 --- a/media/jni/android_media_MediaPlayer2.cpp +++ b/media/jni/android_media_MediaPlayer2.cpp @@ -769,18 +769,6 @@ android_media_MediaPlayer2_seekTo(JNIEnv *env, jobject thiz, jlong msec, jint mo NULL, NULL); } -static void -android_media_MediaPlayer2_notifyAt(JNIEnv *env, jobject thiz, jlong mediaTimeUs) -{ - sp<MediaPlayer2> mp = getMediaPlayer(env, thiz); - if (mp == NULL) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); - return; - } - ALOGV("notifyAt: %lld", (long long)mediaTimeUs); - process_media_player_call( env, thiz, mp->notifyAt((int64_t)mediaTimeUs), NULL, NULL ); -} - static jint android_media_MediaPlayer2_getState(JNIEnv *env, jobject thiz) { @@ -1482,7 +1470,6 @@ static const JNINativeMethod gMethods[] = { {"_setSyncParams", "(Landroid/media/SyncParams;)V", (void *)android_media_MediaPlayer2_setSyncParams}, {"getSyncParams", "()Landroid/media/SyncParams;", (void *)android_media_MediaPlayer2_getSyncParams}, {"_seekTo", "(JI)V", (void *)android_media_MediaPlayer2_seekTo}, - {"_notifyAt", "(J)V", (void *)android_media_MediaPlayer2_notifyAt}, {"_pause", "()V", (void *)android_media_MediaPlayer2_pause}, {"isPlaying", "()Z", (void *)android_media_MediaPlayer2_isPlaying}, {"getCurrentPosition", "()J", (void *)android_media_MediaPlayer2_getCurrentPosition}, diff --git a/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java index 738181d76c2a..b87c9e8de1b2 100644 --- a/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java +++ b/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java @@ -33,13 +33,13 @@ import java.util.Objects; * support message dialog. */ public class RestrictedLockUtils { - public static EnforcedAdmin getProfileOrDeviceOwner(Context context, int userId) { - return getProfileOrDeviceOwner(context, null, userId); + public static EnforcedAdmin getProfileOrDeviceOwner(Context context, UserHandle user) { + return getProfileOrDeviceOwner(context, null, user); } public static EnforcedAdmin getProfileOrDeviceOwner( - Context context, String enforcedRestriction, int userId) { - if (userId == UserHandle.USER_NULL) { + Context context, String enforcedRestriction, UserHandle user) { + if (user == null) { return null; } final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( @@ -47,14 +47,14 @@ public class RestrictedLockUtils { if (dpm == null) { return null; } - ComponentName adminComponent = dpm.getProfileOwnerAsUser(userId); + ComponentName adminComponent = dpm.getProfileOwnerAsUser(user); if (adminComponent != null) { - return new EnforcedAdmin(adminComponent, enforcedRestriction, userId); + return new EnforcedAdmin(adminComponent, enforcedRestriction, user); } - if (dpm.getDeviceOwnerUserId() == userId) { + if (Objects.equals(dpm.getDeviceOwnerUser(), user)) { adminComponent = dpm.getDeviceOwnerComponentOnAnyUser(); if (adminComponent != null) { - return new EnforcedAdmin(adminComponent, enforcedRestriction, userId); + return new EnforcedAdmin(adminComponent, enforcedRestriction, user); } } return null; @@ -66,9 +66,9 @@ public class RestrictedLockUtils { public static void sendShowAdminSupportDetailsIntent(Context context, EnforcedAdmin admin) { final Intent intent = getShowAdminSupportDetailsIntent(context, admin); int targetUserId = UserHandle.myUserId(); - if (admin != null && admin.userId != UserHandle.USER_NULL - && isCurrentUserOrProfile(context, admin.userId)) { - targetUserId = admin.userId; + if (admin != null && admin.user != null + && isCurrentUserOrProfile(context, admin.user.getIdentifier())) { + targetUserId = admin.user.getIdentifier(); } intent.putExtra(DevicePolicyManager.EXTRA_RESTRICTION, admin.enforcedRestriction); context.startActivityAsUser(intent, UserHandle.of(targetUserId)); @@ -81,8 +81,8 @@ public class RestrictedLockUtils { intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, admin.component); } int adminUserId = UserHandle.myUserId(); - if (admin.userId != UserHandle.USER_NULL) { - adminUserId = admin.userId; + if (admin.user != null) { + adminUserId = admin.user.getIdentifier(); } intent.putExtra(Intent.EXTRA_USER_ID, adminUserId); } @@ -109,7 +109,8 @@ public class RestrictedLockUtils { */ @Nullable public String enforcedRestriction = null; - public int userId = UserHandle.USER_NULL; + @Nullable + public UserHandle user = null; // We use this to represent the case where a policy is enforced by multiple admins. public final static EnforcedAdmin MULTIPLE_ENFORCED_ADMIN = new EnforcedAdmin(); @@ -121,15 +122,15 @@ public class RestrictedLockUtils { return enforcedAdmin; } - public EnforcedAdmin(ComponentName component, int userId) { + public EnforcedAdmin(ComponentName component, UserHandle user) { this.component = component; - this.userId = userId; + this.user = user; } - public EnforcedAdmin(ComponentName component, String enforcedRestriction, int userId) { + public EnforcedAdmin(ComponentName component, String enforcedRestriction, UserHandle user) { this.component = component; this.enforcedRestriction = enforcedRestriction; - this.userId = userId; + this.user = user; } public EnforcedAdmin(EnforcedAdmin other) { @@ -138,7 +139,7 @@ public class RestrictedLockUtils { } this.component = other.component; this.enforcedRestriction = other.enforcedRestriction; - this.userId = other.userId; + this.user = other.user; } public EnforcedAdmin() { @@ -149,14 +150,14 @@ public class RestrictedLockUtils { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; EnforcedAdmin that = (EnforcedAdmin) o; - return userId == that.userId && + return Objects.equals(user, that.user) && Objects.equals(component, that.component) && Objects.equals(enforcedRestriction, that.enforcedRestriction); } @Override public int hashCode() { - return Objects.hash(component, enforcedRestriction, userId); + return Objects.hash(component, enforcedRestriction, user); } @Override @@ -164,7 +165,7 @@ public class RestrictedLockUtils { return "EnforcedAdmin{" + "component=" + component + ", enforcedRestriction='" + enforcedRestriction + - ", userId=" + userId + + ", user=" + user + '}'; } } diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java index c03ba9a93294..f57122e6708d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java @@ -164,6 +164,17 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils { } /** + * @return the UserHandle for a userId. Return null for USER_NULL + */ + private static UserHandle getUserHandleOf(@UserIdInt int userId) { + if (userId == UserHandle.USER_NULL) { + return null; + } else { + return UserHandle.of(userId); + } + } + + /** * Filter a set of device admins based on a predicate {@code check}. This is equivalent to * {@code admins.stream().filter(check).map(x → new EnforcedAdmin(admin, userId)} except it's * returning a zero/one/many-type thing. @@ -183,11 +194,13 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils { if (admins == null) { return null; } + + final UserHandle user = getUserHandleOf(userId); EnforcedAdmin enforcedAdmin = null; for (ComponentName admin : admins) { if (check.isEnforcing(dpm, admin, userId)) { if (enforcedAdmin == null) { - enforcedAdmin = new EnforcedAdmin(admin, userId); + enforcedAdmin = new EnforcedAdmin(admin, user); } else { return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; } @@ -211,7 +224,7 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils { IPackageManager ipm = AppGlobals.getPackageManager(); try { if (ipm.getBlockUninstallForUser(packageName, userId)) { - return getProfileOrDeviceOwner(context, userId); + return getProfileOrDeviceOwner(context, getUserHandleOf(userId)); } } catch (RemoteException e) { // Nothing to do @@ -230,7 +243,7 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils { IPackageManager ipm = AppGlobals.getPackageManager(); try { if (ipm.isPackageSuspendedForUser(packageName, userId)) { - return getProfileOrDeviceOwner(context, userId); + return getProfileOrDeviceOwner(context, getUserHandleOf(userId)); } } catch (RemoteException | IllegalArgumentException e) { // Nothing to do @@ -245,14 +258,15 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils { if (dpm == null) { return null; } - EnforcedAdmin admin = getProfileOrDeviceOwner(context, userId); + EnforcedAdmin admin = getProfileOrDeviceOwner(context, getUserHandleOf(userId)); boolean permitted = true; if (admin != null) { permitted = dpm.isInputMethodPermittedByAdmin(admin.component, packageName, userId); } int managedProfileId = getManagedProfileId(context, userId); - EnforcedAdmin profileAdmin = getProfileOrDeviceOwner(context, managedProfileId); + EnforcedAdmin profileAdmin = getProfileOrDeviceOwner(context, + getUserHandleOf(managedProfileId)); boolean permittedByProfileAdmin = true; if (profileAdmin != null) { permittedByProfileAdmin = dpm.isInputMethodPermittedByAdmin(profileAdmin.component, @@ -298,14 +312,15 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils { if (dpm == null) { return null; } - EnforcedAdmin admin = getProfileOrDeviceOwner(context, userId); + EnforcedAdmin admin = getProfileOrDeviceOwner(context, getUserHandleOf(userId)); boolean permitted = true; if (admin != null) { permitted = dpm.isAccessibilityServicePermittedByAdmin(admin.component, packageName, userId); } int managedProfileId = getManagedProfileId(context, userId); - EnforcedAdmin profileAdmin = getProfileOrDeviceOwner(context, managedProfileId); + EnforcedAdmin profileAdmin = getProfileOrDeviceOwner(context, + getUserHandleOf(managedProfileId)); boolean permittedByProfileAdmin = true; if (profileAdmin != null) { permittedByProfileAdmin = dpm.isAccessibilityServicePermittedByAdmin( @@ -365,7 +380,7 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils { if (!isAccountTypeDisabled) { return null; } - return getProfileOrDeviceOwner(context, userId); + return getProfileOrDeviceOwner(context, getUserHandleOf(userId)); } /** @@ -377,7 +392,8 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils { */ public static EnforcedAdmin checkIfMeteredDataRestricted(Context context, String packageName, int userId) { - final EnforcedAdmin enforcedAdmin = getProfileOrDeviceOwner(context, userId); + final EnforcedAdmin enforcedAdmin = getProfileOrDeviceOwner(context, + getUserHandleOf(userId)); if (enforcedAdmin == null) { return null; } @@ -402,7 +418,7 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils { return null; } ComponentName adminComponent = dpm.getDeviceOwnerComponentOnCallingUser(); - return new EnforcedAdmin(adminComponent, UserHandle.myUserId()); + return new EnforcedAdmin(adminComponent, getUserHandleOf(UserHandle.myUserId())); } /** @@ -434,10 +450,11 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils { return null; } EnforcedAdmin enforcedAdmin = null; + final UserHandle user = getUserHandleOf(userId); for (ComponentName admin : admins) { if (check.isEnforcing(dpm, admin, userId)) { if (enforcedAdmin == null) { - enforcedAdmin = new EnforcedAdmin(admin, userId); + enforcedAdmin = new EnforcedAdmin(admin, user); } else { return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; } @@ -488,13 +505,14 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils { if (admins == null) { continue; } + final UserHandle user = getUserHandleOf(userInfo.id); final boolean isSeparateProfileChallengeEnabled = sProxy.isSeparateProfileChallengeEnabled(lockPatternUtils, userInfo.id); for (ComponentName admin : admins) { if (!isSeparateProfileChallengeEnabled) { if (check.isEnforcing(dpm, admin, userInfo.id)) { if (enforcedAdmin == null) { - enforcedAdmin = new EnforcedAdmin(admin, userInfo.id); + enforcedAdmin = new EnforcedAdmin(admin, user); } else { return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; } @@ -511,7 +529,7 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils { DevicePolicyManager parentDpm = sProxy.getParentProfileInstance(dpm, userInfo); if (check.isEnforcing(parentDpm, admin, userInfo.id)) { if (enforcedAdmin == null) { - enforcedAdmin = new EnforcedAdmin(admin, userInfo.id); + enforcedAdmin = new EnforcedAdmin(admin, user); } else { return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; } @@ -535,7 +553,7 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils { ComponentName adminComponent = dpm.getDeviceOwnerComponentOnAnyUser(); if (adminComponent != null) { return new EnforcedAdmin( - adminComponent, enforcedRestriction, dpm.getDeviceOwnerUserId()); + adminComponent, enforcedRestriction, dpm.getDeviceOwnerUser()); } return null; } @@ -556,7 +574,7 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils { } ComponentName adminComponent = dpm.getProfileOwnerAsUser(userId); if (adminComponent != null) { - return new EnforcedAdmin(adminComponent, enforcedRestriction, userId); + return new EnforcedAdmin(adminComponent, enforcedRestriction, getUserHandleOf(userId)); } return null; } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java index fc8d9db20607..88ac8ce5fae5 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java @@ -156,7 +156,7 @@ public class RestrictedLockUtilsTest { final EnforcedAdmin enforcedAdmin = RestrictedLockUtilsInternal .checkIfKeyguardFeaturesDisabled(mContext, KEYGUARD_DISABLE_FINGERPRINT, mUserId); - assertThat(enforcedAdmin).isEqualTo(new EnforcedAdmin(mAdmin1, mUserId)); + assertThat(enforcedAdmin).isEqualTo(new EnforcedAdmin(mAdmin1, UserHandle.of(mUserId))); } @Test @@ -189,12 +189,12 @@ public class RestrictedLockUtilsTest { // Querying the parent should return the policy, since it affects the parent. EnforcedAdmin parent = RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled( mContext, KEYGUARD_DISABLE_FINGERPRINT, mUserId); - assertThat(parent).isEqualTo(new EnforcedAdmin(mAdmin2, mProfileId)); + assertThat(parent).isEqualTo(new EnforcedAdmin(mAdmin2, UserHandle.of(mProfileId))); // Querying the child should return that too. EnforcedAdmin profile = RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled( mContext, KEYGUARD_DISABLE_FINGERPRINT, mProfileId); - assertThat(profile).isEqualTo(new EnforcedAdmin(mAdmin2, mProfileId)); + assertThat(profile).isEqualTo(new EnforcedAdmin(mAdmin2, UserHandle.of(mProfileId))); // Querying for some unrelated feature should return nothing. Nothing! assertThat(RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled( @@ -224,7 +224,7 @@ public class RestrictedLockUtilsTest { // Querying the child should still return the policy. EnforcedAdmin profile = RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled( mContext, KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS, mProfileId); - assertThat(profile).isEqualTo(new EnforcedAdmin(mAdmin2, mProfileId)); + assertThat(profile).isEqualTo(new EnforcedAdmin(mAdmin2, UserHandle.of(mProfileId))); } @Test @@ -251,7 +251,7 @@ public class RestrictedLockUtilsTest { // Querying the child should still return the policy. EnforcedAdmin profile = RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled( mContext, KEYGUARD_DISABLE_FINGERPRINT, mProfileId); - assertThat(profile).isEqualTo(new EnforcedAdmin(mAdmin2, mProfileId)); + assertThat(profile).isEqualTo(new EnforcedAdmin(mAdmin2, UserHandle.of(mProfileId))); } /** @@ -278,7 +278,7 @@ public class RestrictedLockUtilsTest { // Parent should get the policy. EnforcedAdmin parent = RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled( mContext, KEYGUARD_DISABLE_FINGERPRINT, mUserId); - assertThat(parent).isEqualTo(new EnforcedAdmin(mAdmin2, mProfileId)); + assertThat(parent).isEqualTo(new EnforcedAdmin(mAdmin2, UserHandle.of(mProfileId))); // Profile should not get the policy. EnforcedAdmin profile = RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled( diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java index c6a086dcfbdc..c3815e4cee78 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java @@ -14,7 +14,6 @@ package com.android.systemui.shared.plugins; -import android.annotation.Nullable; import android.content.Context; import android.os.Looper; @@ -26,11 +25,16 @@ public interface PluginInitializer { Looper getBgLooper(); /** - * This Runnable is run on the bg looper during initialization of {@link PluginManagerImpl}. + * Called from the bg looper during initialization of {@link PluginManagerImpl}. */ - @Nullable Runnable getBgInitCallback(); + void onPluginManagerInit(); String[] getWhitelistedPlugins(Context context); PluginEnabler getPluginEnabler(Context context); + + /** + * Called from {@link PluginManagerImpl#handleWtfs()}. + */ + void handleWtfs(); } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java index a54e08eba23d..87f29342b167 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java @@ -74,11 +74,11 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage private final boolean isDebuggable; private final PluginPrefs mPluginPrefs; private final PluginEnabler mPluginEnabler; + private final PluginInitializer mPluginInitializer; private ClassLoaderFilter mParentClassLoader; private boolean mListening; private boolean mHasOneShot; private Looper mLooper; - private boolean mWtfsSet; public PluginManagerImpl(Context context, PluginInitializer initializer) { this(context, new PluginInstanceManagerFactory(), Build.IS_DEBUGGABLE, @@ -87,7 +87,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage @VisibleForTesting PluginManagerImpl(Context context, PluginInstanceManagerFactory factory, boolean debuggable, - UncaughtExceptionHandler defaultHandler, PluginInitializer initializer) { + UncaughtExceptionHandler defaultHandler, final PluginInitializer initializer) { mContext = context; mFactory = factory; mLooper = initializer.getBgLooper(); @@ -95,15 +95,18 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage mWhitelistedPlugins.addAll(Arrays.asList(initializer.getWhitelistedPlugins(mContext))); mPluginPrefs = new PluginPrefs(mContext); mPluginEnabler = initializer.getPluginEnabler(mContext); + mPluginInitializer = initializer; PluginExceptionHandler uncaughtExceptionHandler = new PluginExceptionHandler( defaultHandler); Thread.setUncaughtExceptionPreHandler(uncaughtExceptionHandler); - Runnable bgRunnable = initializer.getBgInitCallback(); - if (bgRunnable != null) { - new Handler(mLooper).post(bgRunnable); - } + new Handler(mLooper).post(new Runnable() { + @Override + public void run() { + initializer.onPluginManagerInit(); + } + }); } public String[] getWhitelistedPlugins() { @@ -299,16 +302,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage } public void handleWtfs() { - if (!mWtfsSet) { - mWtfsSet = true; - Log.setWtfHandler(new Log.TerribleFailureHandler() { - @Override - public void onTerribleFailure(String tag, Log.TerribleFailure what, - boolean system) { - throw new CrashWhilePluginActiveException(what); - } - }); - } + mPluginInitializer.handleWtfs(); } public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { @@ -391,7 +385,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage } } - private class CrashWhilePluginActiveException extends RuntimeException { + public static class CrashWhilePluginActiveException extends RuntimeException { public CrashWhilePluginActiveException(Throwable throwable) { super(throwable); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java index 2daa33bb1ea5..112e06785bee 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java @@ -373,11 +373,11 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit mPendingLockCheck.cancel(false); mPendingLockCheck = null; } + displayDefaultSecurityMessage(); } @Override public void onResume(int reason) { - displayDefaultSecurityMessage(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java index ac0a226080bf..774567ef8bb1 100644 --- a/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java @@ -16,29 +16,29 @@ package com.android.systemui.plugins; import android.content.Context; import android.os.Looper; +import android.util.Log; import com.android.systemui.Dependency; +import com.android.systemui.R; import com.android.systemui.shared.plugins.PluginEnabler; import com.android.systemui.shared.plugins.PluginInitializer; -import com.android.systemui.R; +import com.android.systemui.shared.plugins.PluginManagerImpl; public class PluginInitializerImpl implements PluginInitializer { + + private boolean mWtfsSet; + @Override public Looper getBgLooper() { return Dependency.get(Dependency.BG_LOOPER); } @Override - public Runnable getBgInitCallback() { - return new Runnable() { - @Override - public void run() { - // Plugin dependencies that don't have another good home can go here, but - // dependencies that have better places to init can happen elsewhere. - Dependency.get(PluginDependencyProvider.class) - .allowPluginDependency(ActivityStarter.class); - } - }; + public void onPluginManagerInit() { + // Plugin dependencies that don't have another good home can go here, but + // dependencies that have better places to init can happen elsewhere. + Dependency.get(PluginDependencyProvider.class) + .allowPluginDependency(ActivityStarter.class); } @Override @@ -49,4 +49,18 @@ public class PluginInitializerImpl implements PluginInitializer { public PluginEnabler getPluginEnabler(Context context) { return new PluginEnablerImpl(context); } + + @Override + public void handleWtfs() { + if (!mWtfsSet) { + mWtfsSet = true; + Log.setWtfHandler(new Log.TerribleFailureHandler() { + @Override + public void onTerribleFailure(String tag, Log.TerribleFailure what, + boolean system) { + throw new PluginManagerImpl.CrashWhilePluginActiveException(what); + } + }); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index d8f7b6142dd8..7fa042655e53 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -436,7 +436,9 @@ public class NotificationShelf extends ActivatableNotificationView implements public boolean onPreDraw() { boolean animatingY = ViewState.isAnimatingY(icon); if (!animatingY) { - observer.removeOnPreDrawListener(this); + if (observer.isAlive()) { + observer.removeOnPreDrawListener(this); + } icon.setTag(TAG_CONTINUOUS_CLIPPING, null); return true; } @@ -453,7 +455,9 @@ public class NotificationShelf extends ActivatableNotificationView implements @Override public void onViewDetachedFromWindow(View v) { if (v == icon) { - observer.removeOnPreDrawListener(predrawListener); + if (observer.isAlive()) { + observer.removeOnPreDrawListener(predrawListener); + } icon.setTag(TAG_CONTINUOUS_CLIPPING, null); } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewTest.kt index cfe981890bbf..ab3a3e126b34 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewTest.kt @@ -46,9 +46,9 @@ class KeyguardPatternViewTest : SysuiTestCase() { } @Test - fun onResume_clearsTextField() { + fun onPause_clearsTextField() { mSecurityMessage.setMessage("an old message") - mKeyguardPatternView.onResume(KeyguardSecurityView.SCREEN_ON) + mKeyguardPatternView.onPause() assertThat(mSecurityMessage.text).isEqualTo("") } } diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 21f54dd33d3e..7c67596d92b4 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -3754,7 +3754,7 @@ class StorageManagerService extends IStorageManager.Stub } @Override - public void mountExternalStorageForApp(String packageName, int appId, String sharedUserId, + public void prepareSandboxForApp(String packageName, int appId, String sharedUserId, int userId) { final String sandboxId; synchronized (mPackagesLock) { @@ -3771,7 +3771,41 @@ class StorageManagerService extends IStorageManager.Stub } try { - mVold.mountExternalStorageForApp(packageName, appId, sandboxId, userId); + mVold.prepareSandboxForApp(packageName, appId, sandboxId, userId); + } catch (Exception e) { + Slog.wtf(TAG, e); + } + } + + @Override + public void destroySandboxForApp(String packageName, int userId) { + if (!ENABLE_ISOLATED_STORAGE) { + return; + } + final int appId; + final String sandboxId; + synchronized (mPackagesLock) { + final ArraySet<String> userPackages = getAvailablePackagesForUserPL(userId); + userPackages.remove(packageName); + appId = mAppIds.get(packageName); + sandboxId = mSandboxIds.get(appId); + + // If the package is not uninstalled in any other users, remove appId and sandboxId + // corresponding to it from the internal state. + boolean installedInAnyUser = false; + for (int i = mPackages.size() - 1; i >= 0; --i) { + if (mPackages.valueAt(i).contains(packageName)) { + installedInAnyUser = true; + break; + } + } + if (!installedInAnyUser) { + mAppIds.remove(packageName); + mSandboxIds.remove(appId); + } + } + try { + mVold.destroySandboxForApp(packageName, appId, sandboxId, userId); } catch (Exception e) { Slog.wtf(TAG, e); } diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index a392b51d5f53..9c60b8c00019 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -6155,7 +6155,12 @@ public class AccountManagerService final int uid; try { - uid = mPackageManager.getPackageUidAsUser(packageName, userId); + long identityToken = clearCallingIdentity(); + try { + uid = mPackageManager.getPackageUidAsUser(packageName, userId); + } finally { + restoreCallingIdentity(identityToken); + } } catch (NameNotFoundException e) { Slog.e(TAG, "Unknown package " + packageName); return; diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index 389782a91d06..dd993b842aca 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -877,14 +877,17 @@ public class JobSchedulerService extends com.android.server.SystemService // This may throw a SecurityException. jobStatus.prepareLocked(ActivityManager.getService()); - if (toCancel != null) { - cancelJobImplLocked(toCancel, jobStatus, "job rescheduled by app"); - } if (work != null) { // If work has been supplied, enqueue it into the new job. jobStatus.enqueueWorkLocked(ActivityManager.getService(), work); } - startTrackingJobLocked(jobStatus, toCancel); + + if (toCancel != null) { + // Implicitly replaces the existing job record with the new instance + cancelJobImplLocked(toCancel, jobStatus, "job rescheduled by app"); + } else { + startTrackingJobLocked(jobStatus, null); + } StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, uId, null, jobStatus.getBatteryName(), StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED, @@ -1013,6 +1016,12 @@ public class JobSchedulerService extends com.android.server.SystemService } } + /** + * Cancel the given job, stopping it if it's currently executing. If {@code incomingJob} + * is null, the cancelled job is removed outright from the system. If + * {@code incomingJob} is non-null, it replaces {@code cancelled} in the store of + * currently scheduled jobs. + */ private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob, String reason) { if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString()); cancelled.unprepareLocked(ActivityManager.getService()); @@ -1023,6 +1032,11 @@ public class JobSchedulerService extends com.android.server.SystemService } // Cancel if running. stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED, reason); + // If this is a replacement, bring in the new version of the job + if (incomingJob != null) { + if (DEBUG) Slog.i(TAG, "Tracking replacement job " + incomingJob.toShortString()); + startTrackingJobLocked(incomingJob, cancelled); + } reportActiveLocked(); } diff --git a/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java b/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java index 5f082572db58..3ffc5c53eab6 100644 --- a/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java +++ b/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java @@ -16,18 +16,15 @@ package com.android.server.os; -import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; -import android.content.pm.PackageManager; -import android.os.Binder; import android.os.Build; import android.os.IDeviceIdentifiersPolicyService; -import android.os.Process; import android.os.RemoteException; import android.os.SystemProperties; -import android.os.UserHandle; + +import com.android.internal.telephony.TelephonyPermissions; import com.android.server.SystemService; /** @@ -54,15 +51,22 @@ public final class DeviceIdentifiersPolicyService extends SystemService { @Override public @Nullable String getSerial() throws RemoteException { - if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID - && mContext.checkCallingOrSelfPermission( - Manifest.permission.READ_PHONE_STATE) - != PackageManager.PERMISSION_GRANTED - && mContext.checkCallingOrSelfPermission( - Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("getSerial requires READ_PHONE_STATE" - + " or READ_PRIVILEGED_PHONE_STATE permission"); + // Since this invocation is on the server side a null value is used for the + // callingPackage as the server's package name (typically android) should not be used + // for any device / profile owner checks. The majority of requests for the serial number + // should use the getSerialForPackage method with the calling package specified. + if (!TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(mContext, + /* callingPackage */ null, "getSerial")) { + return null; + } + return SystemProperties.get("ro.serialno", Build.UNKNOWN); + } + + @Override + public @Nullable String getSerialForPackage(String callingPackage) throws RemoteException { + if (!TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(mContext, + callingPackage, "getSerial")) { + return null; } return SystemProperties.get("ro.serialno", Build.UNKNOWN); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 329b1da82608..13f084e67494 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -940,6 +940,7 @@ public class PackageManagerService extends IPackageManager.Stub private UserManagerInternal mUserManagerInternal; private ActivityManagerInternal mActivityManagerInternal; private ActivityTaskManagerInternal mActivityTaskManagerInternal; + private StorageManagerInternal mStorageManagerInternal; private DeviceIdleController.LocalService mDeviceIdleController; @@ -4598,6 +4599,13 @@ public class PackageManagerService extends IPackageManager.Stub return mDeviceIdleController; } + private StorageManagerInternal getStorageManagerInternal() { + if (mStorageManagerInternal == null) { + mStorageManagerInternal = LocalServices.getService(StorageManagerInternal.class); + } + return mStorageManagerInternal; + } + /** * Update given flags when being used to request {@link PackageInfo}. */ @@ -9505,6 +9513,30 @@ public class PackageManagerService extends IPackageManager.Stub } catch (InstallerException e) { Slog.w(TAG, String.valueOf(e)); } + // If this package doesn't have a sharedUserId or there are no other packages + // present with same sharedUserId, then delete the sandbox data too. + try { + final SharedUserSetting sharedUserSetting = mSettings.getSharedUserLPw( + pkg.mSharedUserId, 0 /* pkgFlags */, + 0 /* pkgPrivateFlags */, false /* create */); + boolean deleteSandboxData = true; + if (sharedUserSetting != null && sharedUserSetting.packages != null) { + for (int i = sharedUserSetting.packages.size() - 1; i >= 0; --i) { + final PackageSetting packageSetting = sharedUserSetting.packages.valueAt(i); + if (!packageSetting.name.equals(pkg.packageName) + && packageSetting.readUserState(realUserId).isAvailable( + MATCH_UNINSTALLED_PACKAGES)) { + deleteSandboxData = false; + break; + } + } + } + if (deleteSandboxData) { + getStorageManagerInternal().destroySandboxForApp(pkg.packageName, realUserId); + } + } catch (PackageManagerException e) { + // Should not happen + } mDexManager.notifyPackageDataDestroyed(pkg.packageName, userId); } } @@ -19820,9 +19852,7 @@ public class PackageManagerService extends IPackageManager.Stub mDexManager.systemReady(); mPackageDexOptimizer.systemReady(); - StorageManagerInternal storageManagerInternal = LocalServices.getService( - StorageManagerInternal.class); - storageManagerInternal.addExternalStoragePolicy( + getStorageManagerInternal().addExternalStoragePolicy( new StorageManagerInternal.ExternalStorageMountPolicy() { @Override public int getMountMode(int uid, String packageName) { @@ -21215,10 +21245,8 @@ public class PackageManagerService extends IPackageManager.Stub } prepareAppDataContentsLeafLIF(pkg, userId, flags); - final StorageManagerInternal storageManagerInternal - = LocalServices.getService(StorageManagerInternal.class); - if (storageManagerInternal != null) { - storageManagerInternal.mountExternalStorageForApp( + if (getStorageManagerInternal() != null) { + getStorageManagerInternal().prepareSandboxForApp( pkg.packageName, appId, pkg.mSharedUserId, userId); } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java index 66cf48c8ad24..4350596ee63e 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java @@ -66,4 +66,9 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { public long forceSecurityLogs() { return 0; } + + @Override + public boolean checkDeviceIdentifierAccess(String packageName, int userHandle) { + return false; + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index eeb4ad32408c..913b844d8d57 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -7861,6 +7861,21 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return getApplicationLabel(profileOwner.getPackageName(), userHandle); } + @Override + public boolean checkDeviceIdentifierAccess(String packageName, int userHandle) { + // Allow access to the device owner. + ComponentName deviceOwner = getDeviceOwnerComponent(true); + if (deviceOwner != null && deviceOwner.getPackageName().equals(packageName)) { + return true; + } + // Allow access to the profile owner for the specified user. + ComponentName profileOwner = getProfileOwnerAsUser(userHandle); + if (profileOwner != null && profileOwner.getPackageName().equals(packageName)) { + return true; + } + return false; + } + /** * Canonical name for a given package. */ diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index 8d6dccc627cb..9918395005b1 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -50,7 +50,6 @@ import android.hardware.usb.gadget.V1_0.Status; import android.hidl.manager.V1_0.IServiceManager; import android.hidl.manager.V1_0.IServiceNotification; import android.os.BatteryManager; -import android.os.Binder; import android.os.Environment; import android.os.FileUtils; import android.os.Handler; @@ -1982,9 +1981,10 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser * opens the currently attached USB accessory. * * @param accessory accessory to be openened. + * @param uid Uid of the caller */ public ParcelFileDescriptor openAccessory(UsbAccessory accessory, - UsbUserSettingsManager settings) { + UsbUserSettingsManager settings, int uid) { UsbAccessory currentAccessory = mHandler.getCurrentAccessory(); if (currentAccessory == null) { throw new IllegalArgumentException("no accessory attached"); @@ -1995,7 +1995,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser + currentAccessory; throw new IllegalArgumentException(error); } - settings.checkPermission(accessory, Binder.getCallingUid()); + settings.checkPermission(accessory, uid); return nativeOpenAccessory(); } diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java index 4be70af05a3f..e0f36852c867 100644 --- a/services/usb/java/com/android/server/usb/UsbService.java +++ b/services/usb/java/com/android/server/usb/UsbService.java @@ -230,42 +230,29 @@ public class UsbService extends IUsbManager.Stub { } } - /** - * Check if the calling user is in the same profile group as the {@link #mCurrentUserId - * current user}. - * - * @return Iff the caller is in the current user's profile group - */ - @GuardedBy("mLock") - private boolean isCallerInCurrentUserProfileGroupLocked() { - int userIdInt = UserHandle.getCallingUserId(); - - long ident = clearCallingIdentity(); - try { - return mUserManager.isSameProfileGroup(userIdInt, mCurrentUserId); - } finally { - restoreCallingIdentity(ident); - } - } - /* Opens the specified USB device (host mode) */ @Override public ParcelFileDescriptor openDevice(String deviceName, String packageName) { ParcelFileDescriptor fd = null; if (mHostManager != null) { - synchronized (mLock) { - if (deviceName != null) { - int userIdInt = UserHandle.getCallingUserId(); - boolean isCurrentUser = isCallerInCurrentUserProfileGroupLocked(); - - if (isCurrentUser) { - fd = mHostManager.openDevice(deviceName, getSettingsForUser(userIdInt), - packageName, Binder.getCallingUid()); - } else { - Slog.w(TAG, "Cannot open " + deviceName + " for user " + userIdInt + - " as user is not active."); + if (deviceName != null) { + int uid = Binder.getCallingUid(); + int user = UserHandle.getUserId(uid); + + long ident = clearCallingIdentity(); + try { + synchronized (mLock) { + if (mUserManager.isSameProfileGroup(user, mCurrentUserId)) { + fd = mHostManager.openDevice(deviceName, getSettingsForUser(user), + packageName, uid); + } else { + Slog.w(TAG, "Cannot open " + deviceName + " for user " + user + + " as user is not active."); + } } + } finally { + restoreCallingIdentity(ident); } } } @@ -287,17 +274,22 @@ public class UsbService extends IUsbManager.Stub { @Override public ParcelFileDescriptor openAccessory(UsbAccessory accessory) { if (mDeviceManager != null) { - int userIdInt = UserHandle.getCallingUserId(); - - synchronized (mLock) { - boolean isCurrentUser = isCallerInCurrentUserProfileGroupLocked(); - - if (isCurrentUser) { - return mDeviceManager.openAccessory(accessory, getSettingsForUser(userIdInt)); - } else { - Slog.w(TAG, "Cannot open " + accessory + " for user " + userIdInt + - " as user is not active."); + int uid = Binder.getCallingUid(); + int user = UserHandle.getUserId(uid); + + long ident = clearCallingIdentity(); + try { + synchronized (mLock) { + if (mUserManager.isSameProfileGroup(user, mCurrentUserId)) { + return mDeviceManager.openAccessory(accessory, getSettingsForUser(user), + uid); + } else { + Slog.w(TAG, "Cannot open " + accessory + " for user " + user + + " as user is not active."); + } } + } finally { + restoreCallingIdentity(ident); } } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 52c8f5a58ebe..c5e47077e680 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -1266,15 +1266,18 @@ public class TelephonyManager { * Returns the unique device ID, for example, the IMEI for GSM and the MEID * or ESN for CDMA phones. Return null if device ID is not available. * - * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} - * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the + * device or profile owner. The profile owner is an app that owns a managed profile on the + * device; for more details see <a href="https://developer.android.com/work/managed-profiles"> + * Work profiles</a>. Profile owner access is deprecated and will be removed in a future + * release. * * @deprecated Use (@link getImei} which returns IMEI for GSM or (@link getMeid} which returns * MEID for CDMA. */ @Deprecated - @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @SuppressAutoDoc // No support for device / profile owner. + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDeviceId() { try { ITelephony telephony = getITelephony(); @@ -1292,8 +1295,11 @@ public class TelephonyManager { * Returns the unique device ID of a subscription, for example, the IMEI for * GSM and the MEID for CDMA phones. Return null if device ID is not available. * - * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} - * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the + * device or profile owner. The profile owner is an app that owns a managed profile on the + * device; for more details see <a href="https://developer.android.com/work/managed-profiles"> + * Work profiles</a>. Profile owner access is deprecated and will be removed in a future + * release. * * @param slotIndex of which deviceID is returned * @@ -1301,8 +1307,8 @@ public class TelephonyManager { * MEID for CDMA. */ @Deprecated - @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @SuppressAutoDoc // No support for device / profile owner. + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDeviceId(int slotIndex) { // FIXME this assumes phoneId == slotIndex try { @@ -1321,11 +1327,14 @@ public class TelephonyManager { * Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not * available. * - * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} - * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the + * device or profile owner. The profile owner is an app that owns a managed profile on the + * device; for more details see <a href="https://developer.android.com/work/managed-profiles"> + * Work profiles</a>. Profile owner access is deprecated and will be removed in a future + * release. */ - @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @SuppressAutoDoc // No support for device / profile owner. + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getImei() { return getImei(getSlotIndex()); } @@ -1334,13 +1343,16 @@ public class TelephonyManager { * Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not * available. * - * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} - * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the + * device or profile owner. The profile owner is an app that owns a managed profile on the + * device; for more details see <a href="https://developer.android.com/work/managed-profiles"> + * Work profiles</a>. Profile owner access is deprecated and will be removed in a future + * release. * * @param slotIndex of which IMEI is returned */ - @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @SuppressAutoDoc // No support for device / profile owner. + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getImei(int slotIndex) { ITelephony telephony = getITelephony(); if (telephony == null) return null; @@ -1384,11 +1396,14 @@ public class TelephonyManager { /** * Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available. * - * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} - * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the + * device or profile owner. The profile owner is an app that owns a managed profile on the + * device; for more details see <a href="https://developer.android.com/work/managed-profiles"> + * Work profiles</a>. Profile owner access is deprecated and will be removed in a future + * release. */ - @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @SuppressAutoDoc // No support for device / profile owner. + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getMeid() { return getMeid(getSlotIndex()); } @@ -1396,13 +1411,16 @@ public class TelephonyManager { /** * Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available. * - * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} - * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the + * device or profile owner. The profile owner is an app that owns a managed profile on the + * device; for more details see <a href="https://developer.android.com/work/managed-profiles"> + * Work profiles</a>. Profile owner access is deprecated and will be removed in a future + * release. * * @param slotIndex of which MEID is returned */ - @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @SuppressAutoDoc // No support for device / profile owner. + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getMeid(int slotIndex) { ITelephony telephony = getITelephony(); if (telephony == null) return null; @@ -2886,11 +2904,15 @@ public class TelephonyManager { * Returns the serial number of the SIM, if applicable. Return null if it is * unavailable. * - * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} - * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or + * profile owner, or that the calling app has carrier privileges (see {@link + * #hasCarrierPrivileges}). The profile owner is an app that owns a managed profile on the + * device; for more details see <a href="https://developer.android.com/work/managed-profiles"> + * Work profiles</a>. Profile owner access is deprecated and will be removed in a future + * release. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getSimSerialNumber() { return getSimSerialNumber(getSubId()); } @@ -2898,11 +2920,18 @@ public class TelephonyManager { /** * Returns the serial number for the given subscription, if applicable. Return null if it is * unavailable. - * <p> + * + * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or + * profile owner, or that the calling app has carrier privileges (see {@link + * #hasCarrierPrivileges}). The profile owner is an app that owns a managed profile on the + * device; for more details see <a href="https://developer.android.com/work/managed-profiles"> + * Work profiles</a>. Profile owner access is deprecated and will be removed in a future + * release. + * * @param subId for which Sim Serial number is returned * @hide */ - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @UnsupportedAppUsage public String getSimSerialNumber(int subId) { try { @@ -3037,11 +3066,15 @@ public class TelephonyManager { * Returns the unique subscriber ID, for example, the IMSI for a GSM phone. * Return null if it is unavailable. * - * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} - * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or + * profile owner, or that the calling app has carrier privileges (see {@link + * #hasCarrierPrivileges}). The profile owner is an app that owns a managed profile on the + * device; for more details see <a href="https://developer.android.com/work/managed-profiles"> + * Work profiles</a>. Profile owner access is deprecated and will be removed in a future + * release. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getSubscriberId() { return getSubscriberId(getSubId()); } @@ -3051,10 +3084,17 @@ public class TelephonyManager { * for a subscription. * Return null if it is unavailable. * + * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or + * profile owner, or that the calling app has carrier privileges (see {@link + * #hasCarrierPrivileges}). The profile owner is an app that owns a managed profile on the + * device; for more details see <a href="https://developer.android.com/work/managed-profiles"> + * Work profiles</a>. Profile owner access is deprecated and will be removed in a future + * release. + * * @param subId whose subscriber id is returned * @hide */ - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @UnsupportedAppUsage public String getSubscriberId(int subId) { try { diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java index 23ea237a32eb..dac7e04be07a 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java +++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java @@ -19,11 +19,16 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; import android.Manifest; import android.app.AppOpsManager; +import android.app.admin.DevicePolicyManager; import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.os.Binder; +import android.os.Build; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.UserHandle; import android.telephony.Rlog; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; @@ -135,6 +140,169 @@ public final class TelephonyPermissions { } /** + * Check whether the caller (or self, if not processing an IPC) can read device identifiers. + * + * <p>This method behaves in one of the following ways: + * <ul> + * <li>return true: if the caller has the READ_PRIVILEGED_PHONE_STATE permission or the + * calling package passes a DevicePolicyManager Device Owner / Profile Owner device + * identifier access check, + * <li>throw SecurityException: if the caller does not meet any of the requirements and is + * targeting Q or is targeting pre-Q and does not have the READ_PHONE_STATE permission. + * <li>return false: if the caller is targeting pre-Q and does have the READ_PHONE_STATE + * permission. In this case the caller would expect to have access to the device + * identifiers so false is returned instead of throwing a SecurityException to indicate + * the calling function should return dummy data. + * </ul> + */ + public static boolean checkCallingOrSelfReadDeviceIdentifiers(Context context, + String callingPackage, String message) { + return checkCallingOrSelfReadDeviceIdentifiers(context, + SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage, message); + } + + /** + * Check whether the caller (or self, if not processing an IPC) can read device identifiers. + * + * <p>This method behaves in one of the following ways: + * <ul> + * <li>return true: if the caller has the READ_PRIVILEGED_PHONE_STATE permission or the + * calling package passes a DevicePolicyManager Device Owner / Profile Owner device + * identifier access check, + * <li>throw SecurityException: if the caller does not meet any of the requirements and is + * targeting Q or is targeting pre-Q and does not have the READ_PHONE_STATE permission + * or carrier privileges. + * <li>return false: if the caller is targeting pre-Q and does have the READ_PHONE_STATE + * permission or carrier privileges. In this case the caller would expect to have access + * to the device identifiers so false is returned instead of throwing a SecurityException + * to indicate the calling function should return dummy data. + * </ul> + */ + public static boolean checkCallingOrSelfReadDeviceIdentifiers(Context context, int subId, + String callingPackage, String message) { + int pid = Binder.getCallingPid(); + int uid = Binder.getCallingUid(); + // if the device identifier check completes successfully then grant access. + if (checkReadDeviceIdentifiers(context, pid, uid, callingPackage)) { + return true; + } + // else the calling package is not authorized to access the device identifiers; call + // a central method to report the failure based on the target SDK and if the calling package + // has the READ_PHONE_STATE permission or carrier privileges that were previously required + // to access the identifiers. + return reportAccessDeniedToReadIdentifiers(context, subId, pid, uid, callingPackage, + message); + } + + /** + * Check whether the caller (or self, if not processing an IPC) can read subscriber identifiers. + * + * <p>This method behaves in one of the following ways: + * <ul> + * <li>return true: if the caller has the READ_PRIVILEGED_PHONE_STATE permission, the calling + * package passes a DevicePolicyManager Device Owner / Profile Owner device identifier + * access check, or the calling package has carrier privleges. + * <li>throw SecurityException: if the caller does not meet any of the requirements and is + * targeting Q or is targeting pre-Q and does not have the READ_PHONE_STATE permission. + * <li>return false: if the caller is targeting pre-Q and does have the READ_PHONE_STATE + * permission. In this case the caller would expect to have access to the device + * identifiers so false is returned instead of throwing a SecurityException to indicate + * the calling function should return dummy data. + * </ul> + */ + public static boolean checkCallingOrSelfReadSubscriberIdentifiers(Context context, int subId, + String callingPackage, String message) { + int pid = Binder.getCallingPid(); + int uid = Binder.getCallingUid(); + // if the device identifiers can be read then grant access to the subscriber identifiers + if (checkReadDeviceIdentifiers(context, pid, uid, callingPackage)) { + return true; + } + // If the calling package has carrier privileges then allow access to the subscriber + // identifiers. + if (SubscriptionManager.isValidSubscriptionId(subId) && getCarrierPrivilegeStatus( + TELEPHONY_SUPPLIER, subId, uid) + == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { + return true; + } + return reportAccessDeniedToReadIdentifiers(context, subId, pid, uid, callingPackage, + message); + } + + /** + * Checks whether the app with the given pid/uid can read device identifiers. + * + * @returns true if the caller has the READ_PRIVILEGED_PHONE_STATE permission or the calling + * package passes a DevicePolicyManager Device Owner / Profile Owner device identifier access + * check. + */ + private static boolean checkReadDeviceIdentifiers(Context context, int pid, int uid, + String callingPackage) { + // Allow system and root access to the device identifiers. + final int appId = UserHandle.getAppId(uid); + if (appId == Process.SYSTEM_UID || appId == Process.ROOT_UID) { + return true; + } + // Allow access to packages that have the READ_PRIVILEGED_PHONE_STATE permission. + if (context.checkPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, pid, + uid) == PackageManager.PERMISSION_GRANTED) { + return true; + } + // if the calling package is null then return now as there's no way to perform the + // DevicePolicyManager device / profile owner checks. + if (callingPackage == null) { + return false; + } + // Allow access to a device / profile owner app. + DevicePolicyManager devicePolicyManager = (DevicePolicyManager) context.getSystemService( + Context.DEVICE_POLICY_SERVICE); + if (devicePolicyManager != null && devicePolicyManager.checkDeviceIdentifierAccessAsUser( + callingPackage, Binder.getCallingUserHandle().getIdentifier())) { + return true; + } + return false; + } + + /** + * Reports a failure when the app with the given pid/uid cannot access the requested identifier. + * + * @returns false if the caller is targeting pre-Q and does have the READ_PHONE_STATE + * permission or carrier privileges. + * @throws SecurityException if the caller does not meet any of the requirements for the + * requested identifier and is targeting Q or is targeting pre-Q + * and does not have the READ_PHONE_STATE permission or carrier + * privileges. + */ + private static boolean reportAccessDeniedToReadIdentifiers(Context context, int subId, int pid, + int uid, String callingPackage, String message) { + if (callingPackage != null) { + try { + // if the target SDK is pre-Q then check if the calling package would have + // previously had access to device identifiers. + ApplicationInfo callingPackageInfo = context.getPackageManager().getApplicationInfo( + callingPackage, 0); + if (callingPackageInfo != null + && callingPackageInfo.targetSdkVersion < Build.VERSION_CODES.Q) { + if (context.checkPermission(android.Manifest.permission.READ_PHONE_STATE, pid, + uid) == PackageManager.PERMISSION_GRANTED) { + return false; + } + if (SubscriptionManager.isValidSubscriptionId(subId) + && getCarrierPrivilegeStatus(TELEPHONY_SUPPLIER, subId, uid) + == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { + return false; + } + } + } catch (PackageManager.NameNotFoundException e) { + // If the application info for the calling package could not be found then default + // to throwing the SecurityException. + } + } + throw new SecurityException(message + ": The user " + uid + " does not have the " + + "READ_PRIVILEGED_PHONE_STATE permission to access the device identifiers"); + } + + /** * Check whether the app with the given pid/uid can read the call log. * @return {@code true} if the specified app has the read call log permission and AppOpp granted * to it, {@code false} otherwise. |