Merge "Tint lock icon when entering ambient mode"
diff --git a/api/system-current.txt b/api/system-current.txt
index 30a0c85..ac8fc9c 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -523,11 +523,11 @@
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 @@
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 a1b8d86..74fb4df 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -5224,13 +5224,30 @@
}
/**
- * @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 @@
@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 @@
}
/**
+ * 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 35ea250..5e45450 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -153,6 +153,8 @@
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 c1ca636..fefb8d3 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -74,6 +74,7 @@
ComponentName mActivityComponent;
private boolean mIsHomeActivity;
private int mFlags;
+ private int mAutofillFlags;
final ArrayList<WindowNode> mWindowNodes = new ArrayList<>();
@@ -197,6 +198,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
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 @@
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 @@
}
out.writeInt(writtenFlags);
+ out.writeInt(autofillFlags);
if ((flags&FLAGS_HAS_ID) != 0) {
out.writeInt(mId);
if (mId != View.NO_ID) {
@@ -883,32 +949,51 @@
}
}
- 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.writeParcelable(sanitizedValue, 0);
- out.writeCharSequenceArray(mAutofillOptions);
- if (mHtmlInfo instanceof Parcelable) {
- 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);
+ 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);
+ }
+ 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);
+ }
+ 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 8681893..26c2033 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -18,8 +18,10 @@
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 @@
* <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 ac19f2b..87d358f 100644
--- a/core/java/android/os/IDeviceIdentifiersPolicyService.aidl
+++ b/core/java/android/os/IDeviceIdentifiersPolicyService.aidl
@@ -21,4 +21,5 @@
*/
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 4fe2d58..f8feb7b 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -61,7 +61,6 @@
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 1282170..8123744 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -268,7 +268,7 @@
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 1f54ea5..63ff7b2 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 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 mountExternalStorageForApp(String packageName, int appId,
- String sharedUserId, int userId);
+ 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 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 4a58f88..2989df8 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 @@
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 bdb3e08..fe51a39 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.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 @@
// 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 @@
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 @@
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 b5d9952..ed24543 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -323,6 +323,7 @@
<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 494e513..e3ec45b 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -61,6 +61,7 @@
shared_libs: [
"liblog",
"libcutils",
+ "libstatslog",
"libutils",
"libEGL",
"libGLESv2",
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index f2d50cd..e7ae767 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 @@
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 6594bd22..d746978 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 @@
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 3c48d36..26cfa90 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 @@
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 @@
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 0000000..8344469
--- /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 cdf31da..65b4e26 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 @@
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 @@
});
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 @@
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 @@
// 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 474d8b9..889a5f7 100644
--- a/media/java/android/media/MediaCrypto.java
+++ b/media/java/android/media/MediaCrypto.java
@@ -56,13 +56,15 @@
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 850be78..6d122d7 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -1202,7 +1202,6 @@
* 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 d2989f0..db6da8c 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -32,9 +32,6 @@
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 @@
* 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 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 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 @@
// 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 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 @@
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 2a03a89..0e5cbe4 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -19,7 +19,6 @@
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.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.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.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 @@
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 @@
*/
@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 @@
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 @@
}
};
- // 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 @@
@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 @@
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 @@
*/
@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 @@
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 @@
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 @@
mHandlerThread.quitSafely();
mHandlerThread = null;
}
- if (mTimeProvider != null) {
- mTimeProvider.close();
- mTimeProvider = null;
- }
// Modular DRM clean up
mOnDrmConfigHelper = null;
@@ -2503,17 +1934,6 @@
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 @@
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 @@
}
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 @@
processPendingTask_l();
}
}
- }
- // fall through
-
- case MEDIA_SKIPPED:
- {
- TimeProvider timeProvider = mTimeProvider;
- if (timeProvider != null) {
- timeProvider.onSeekComplete(mMediaPlayer);
- }
return;
}
@@ -2762,33 +2154,6 @@
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 @@
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 @@
}
}
- /** @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 693a3d0..b52da36 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -769,18 +769,6 @@
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 @@
{"_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 738181d..b87c9e8 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 @@
* 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 @@
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 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 @@
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 @@
*/
@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 @@
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 @@
}
this.component = other.component;
this.enforcedRestriction = other.enforcedRestriction;
- this.userId = other.userId;
+ this.user = other.user;
}
public EnforcedAdmin() {
@@ -149,14 +150,14 @@
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 @@
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 c03ba9a..f57122e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -164,6 +164,17 @@
}
/**
+ * @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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
if (!isAccountTypeDisabled) {
return null;
}
- return getProfileOrDeviceOwner(context, userId);
+ return getProfileOrDeviceOwner(context, getUserHandleOf(userId));
}
/**
@@ -377,7 +392,8 @@
*/
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 @@
return null;
}
ComponentName adminComponent = dpm.getDeviceOwnerComponentOnCallingUser();
- return new EnforcedAdmin(adminComponent, UserHandle.myUserId());
+ return new EnforcedAdmin(adminComponent, getUserHandleOf(UserHandle.myUserId()));
}
/**
@@ -434,10 +450,11 @@
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 @@
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 @@
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 @@
ComponentName adminComponent = dpm.getDeviceOwnerComponentOnAnyUser();
if (adminComponent != null) {
return new EnforcedAdmin(
- adminComponent, enforcedRestriction, dpm.getDeviceOwnerUserId());
+ adminComponent, enforcedRestriction, dpm.getDeviceOwnerUser());
}
return null;
}
@@ -556,7 +574,7 @@
}
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 fc8d9db..88ac8ce 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 @@
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 @@
// 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 @@
// 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 @@
// 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 @@
// 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 c6a086d..c3815e4 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 @@
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 a54e08e..87f2934 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 @@
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 @@
@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 @@
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 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 @@
}
}
- 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 2daa33b..112e067 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -373,11 +373,11 @@
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 ac0a226..774567e 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 @@
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 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 d8f7b61..7fa0426 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 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 @@
@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 cfe9818..ab3a3e1 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 @@
}
@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 21f54dd..7c67596 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3754,7 +3754,7 @@
}
@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 @@
}
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 a392b51..9c60b8c 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 @@
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 389782a..dd993b8 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 @@
// 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 @@
}
}
+ /**
+ * 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 @@
}
// 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 5f08257..3ffc5c5 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 @@
@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 329b1da..13f084e 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 @@
private UserManagerInternal mUserManagerInternal;
private ActivityManagerInternal mActivityManagerInternal;
private ActivityTaskManagerInternal mActivityTaskManagerInternal;
+ private StorageManagerInternal mStorageManagerInternal;
private DeviceIdleController.LocalService mDeviceIdleController;
@@ -4598,6 +4599,13 @@
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 @@
} 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 @@
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 @@
}
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 66cf48c..4350596 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 @@
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 eeb4ad3..913b844 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 @@
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 8d6dccc..9918395 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.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 @@
* 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 @@
+ 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 4be70af..e0f3685 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 @@
}
}
- /**
- * 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 (deviceName != null) {
+ int uid = Binder.getCallingUid();
+ int user = UserHandle.getUserId(uid);
- 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.");
+ 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 @@
@Override
public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
if (mDeviceManager != null) {
- int userIdInt = UserHandle.getCallingUserId();
+ int uid = Binder.getCallingUid();
+ int user = UserHandle.getUserId(uid);
- 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.");
+ 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 52c8f5a..c5e4707 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1266,15 +1266,18 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
/**
* 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 @@
/**
* 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 @@
* 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 @@
/**
* 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 @@
* 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 @@
* 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 23ea237..dac7e04 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
@@ -19,11 +19,16 @@
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 @@
}
/**
+ * 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.