summaryrefslogtreecommitdiff
path: root/core/java/android/content
diff options
context:
space:
mode:
authorXin Li <delphij@google.com>2018-08-06 19:19:55 -0700
committerXin Li <delphij@google.com>2018-08-06 19:20:02 -0700
commit02857a72198613a0583cdf6863edb2df59beee04 (patch)
tree076fcfdb52deea3aada1c0dd8b31decbd87c80b0 /core/java/android/content
parent10c593cf7f30ddea32889361c81ef06eabaeb6b3 (diff)
parent7e1e76a6dc088458b159c4c89c54b78d32d7d310 (diff)
Merge Android Pie into master
Bug: 112104996 Change-Id: Id91836f22f2c9842975ac5b55f0f18b89db9b2f5
Diffstat (limited to 'core/java/android/content')
-rw-r--r--core/java/android/content/AbstractThreadedSyncAdapter.java61
-rw-r--r--core/java/android/content/AsyncTaskLoader.java4
-rw-r--r--core/java/android/content/ClipData.java43
-rw-r--r--core/java/android/content/ClipDescription.java23
-rw-r--r--core/java/android/content/ClipboardManager.java54
-rw-r--r--core/java/android/content/ComponentName.java66
-rw-r--r--core/java/android/content/ContentProviderClient.java13
-rw-r--r--core/java/android/content/ContentResolver.java184
-rw-r--r--core/java/android/content/Context.java553
-rw-r--r--core/java/android/content/ContextWrapper.java32
-rw-r--r--core/java/android/content/CursorLoader.java6
-rw-r--r--core/java/android/content/IClipboard.aidl1
-rw-r--r--core/java/android/content/IContentService.aidl2
-rw-r--r--core/java/android/content/ISyncAdapter.aidl9
-rw-r--r--core/java/android/content/ISyncAdapterUnsyncableAccountCallback.aidl30
-rw-r--r--core/java/android/content/Intent.java541
-rw-r--r--core/java/android/content/IntentFilter.java68
-rw-r--r--core/java/android/content/Loader.java15
-rw-r--r--core/java/android/content/PermissionChecker.java173
-rw-r--r--core/java/android/content/QuickViewConstants.java11
-rw-r--r--core/java/android/content/ServiceConnection.java22
-rw-r--r--core/java/android/content/SharedPreferences.java5
-rw-r--r--core/java/android/content/SyncResult.java12
-rw-r--r--core/java/android/content/SyncStatusInfo.java261
-rw-r--r--core/java/android/content/om/IOverlayManager.aidl25
-rw-r--r--core/java/android/content/om/OverlayInfo.java119
-rw-r--r--core/java/android/content/pm/ActivityInfo.java134
-rw-r--r--core/java/android/content/pm/AndroidTestBaseUpdater.java54
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java415
-rw-r--r--core/java/android/content/pm/AuxiliaryResolveInfo.java99
-rw-r--r--core/java/android/content/pm/ComponentInfo.java3
-rw-r--r--core/java/android/content/pm/CrossProfileApps.java152
-rw-r--r--core/java/android/content/pm/EphemeralIntentFilter.java85
-rw-r--r--core/java/android/content/pm/EphemeralResolveInfo.java224
-rw-r--r--core/java/android/content/pm/FeatureInfo.java13
-rw-r--r--core/java/android/content/pm/ICrossProfileApps.aidl33
-rw-r--r--core/java/android/content/pm/ILauncherApps.aidl6
-rw-r--r--core/java/android/content/pm/IOnAppsChangedListener.aidl4
-rw-r--r--core/java/android/content/pm/IPackageInstallObserver.aidl27
-rw-r--r--core/java/android/content/pm/IPackageInstallerSession.aidl3
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl48
-rw-r--r--core/java/android/content/pm/InstantAppRequest.java10
-rw-r--r--core/java/android/content/pm/InstantAppResolveInfo.java189
-rw-r--r--core/java/android/content/pm/InstrumentationInfo.java14
-rw-r--r--core/java/android/content/pm/LauncherApps.java170
-rw-r--r--core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java48
-rw-r--r--core/java/android/content/pm/PackageBackwardCompatibility.java194
-rw-r--r--core/java/android/content/pm/PackageInfo.java142
-rw-r--r--core/java/android/content/pm/PackageInfoLite.java28
-rw-r--r--core/java/android/content/pm/PackageInstaller.java78
-rw-r--r--core/java/android/content/pm/PackageItemInfo.java332
-rw-r--r--core/java/android/content/pm/PackageList.java74
-rw-r--r--core/java/android/content/pm/PackageManager.java698
-rw-r--r--core/java/android/content/pm/PackageManagerInternal.java249
-rw-r--r--core/java/android/content/pm/PackageParser.java1409
-rw-r--r--core/java/android/content/pm/PackageSharedLibraryUpdater.java107
-rw-r--r--core/java/android/content/pm/PackageUserState.java35
-rw-r--r--core/java/android/content/pm/PermissionGroupInfo.java14
-rw-r--r--core/java/android/content/pm/PermissionInfo.java180
-rw-r--r--core/java/android/content/pm/RegisteredServicesCache.java58
-rw-r--r--core/java/android/content/pm/ResolveInfo.java36
-rw-r--r--core/java/android/content/pm/SharedLibraryInfo.java31
-rw-r--r--core/java/android/content/pm/SharedLibraryNames.java32
-rw-r--r--core/java/android/content/pm/ShortcutInfo.java323
-rw-r--r--core/java/android/content/pm/ShortcutManager.java209
-rw-r--r--core/java/android/content/pm/ShortcutServiceInternal.java13
-rw-r--r--core/java/android/content/pm/Signature.java23
-rw-r--r--core/java/android/content/pm/SigningInfo.java139
-rw-r--r--core/java/android/content/pm/VerifierDeviceIdentity.java3
-rw-r--r--core/java/android/content/pm/VersionedPackage.java28
-rw-r--r--core/java/android/content/pm/dex/ArtManager.java28
-rw-r--r--core/java/android/content/pm/dex/ArtManagerInternal.java34
-rw-r--r--core/java/android/content/pm/dex/IArtManager.aidl6
-rw-r--r--core/java/android/content/pm/dex/PackageOptimizationInfo.java47
-rw-r--r--core/java/android/content/pm/permission/RuntimePermissionPresenter.java1
-rw-r--r--core/java/android/content/pm/split/DefaultSplitAssetLoader.java75
-rw-r--r--core/java/android/content/pm/split/SplitAssetDependencyLoader.java88
-rw-r--r--core/java/android/content/res/ApkAssets.java195
-rw-r--r--core/java/android/content/res/AssetFileDescriptor.java4
-rw-r--r--core/java/android/content/res/AssetManager.java1413
-rw-r--r--core/java/android/content/res/Configuration.java305
-rw-r--r--core/java/android/content/res/FontResourcesParser.java23
-rw-r--r--core/java/android/content/res/GradientColor.java7
-rw-r--r--core/java/android/content/res/Resources.java43
-rw-r--r--core/java/android/content/res/ResourcesImpl.java133
-rw-r--r--core/java/android/content/res/TypedArray.java185
-rw-r--r--core/java/android/content/res/XmlBlock.java8
-rw-r--r--core/java/android/content/res/XmlResourceParser.java2
88 files changed, 8297 insertions, 2799 deletions
diff --git a/core/java/android/content/AbstractThreadedSyncAdapter.java b/core/java/android/content/AbstractThreadedSyncAdapter.java
index 2629929e91ce..b528e397906f 100644
--- a/core/java/android/content/AbstractThreadedSyncAdapter.java
+++ b/core/java/android/content/AbstractThreadedSyncAdapter.java
@@ -16,11 +16,17 @@
package android.content;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
import android.accounts.Account;
+import android.annotation.MainThread;
+import android.annotation.NonNull;
import android.os.Build;
import android.os.Bundle;
+import android.os.Handler;
import android.os.IBinder;
import android.os.Process;
+import android.os.RemoteException;
import android.os.Trace;
import android.util.Log;
@@ -166,6 +172,13 @@ public abstract class AbstractThreadedSyncAdapter {
private class ISyncAdapterImpl extends ISyncAdapter.Stub {
@Override
+ public void onUnsyncableAccount(ISyncAdapterUnsyncableAccountCallback cb) {
+ Handler.getMain().sendMessage(obtainMessage(
+ AbstractThreadedSyncAdapter::handleOnUnsyncableAccount,
+ AbstractThreadedSyncAdapter.this, cb));
+ }
+
+ @Override
public void startSync(ISyncContext syncContext, String authority, Account account,
Bundle extras) {
if (ENABLE_LOG) {
@@ -374,6 +387,54 @@ public abstract class AbstractThreadedSyncAdapter {
}
/**
+ * Handle a call of onUnsyncableAccount.
+ *
+ * @param cb The callback to report the return value to
+ */
+ private void handleOnUnsyncableAccount(@NonNull ISyncAdapterUnsyncableAccountCallback cb) {
+ boolean doSync;
+ try {
+ doSync = onUnsyncableAccount();
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Exception while calling onUnsyncableAccount, assuming 'true'", e);
+ doSync = true;
+ }
+
+ try {
+ cb.onUnsyncableAccountDone(doSync);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not report result of onUnsyncableAccount", e);
+ }
+ }
+
+ /**
+ * Allows to defer syncing until all accounts are properly set up.
+ *
+ * <p>Called when a account / authority pair
+ * <ul>
+ * <li>that can be handled by this adapter</li>
+ * <li>{@link ContentResolver#requestSync(SyncRequest) is synced}</li>
+ * <li>and the account/provider {@link ContentResolver#getIsSyncable(Account, String) has
+ * unknown state (<0)}.</li>
+ * </ul>
+ *
+ * <p>This might be called on a different service connection as {@link #onPerformSync}.
+ *
+ * <p>The system expects this method to immediately return. If the call stalls the system
+ * behaves as if this method returned {@code true}. If it is required to perform a longer task
+ * (such as interacting with the user), return {@code false} and proceed in a difference
+ * context, such as an {@link android.app.Activity}, or foreground service. The sync can then be
+ * rescheduled once the account becomes syncable.
+ *
+ * @return If {@code false} syncing is deferred. Returns {@code true} by default, i.e. by
+ * default syncing starts immediately.
+ */
+ @MainThread
+ public boolean onUnsyncableAccount() {
+ return true;
+ }
+
+ /**
* Perform a sync for this account. SyncAdapter-specific parameters may
* be specified in extras, which is guaranteed to not be null. Invocations
* of this method are guaranteed to be serialized.
diff --git a/core/java/android/content/AsyncTaskLoader.java b/core/java/android/content/AsyncTaskLoader.java
index b7545bf55b58..c44e35687909 100644
--- a/core/java/android/content/AsyncTaskLoader.java
+++ b/core/java/android/content/AsyncTaskLoader.java
@@ -49,7 +49,11 @@ import java.util.concurrent.Executor;
* fragment}
*
* @param <D> the data type to be loaded.
+ *
+ * @deprecated Use the <a href="{@docRoot}tools/extras/support-library.html">Support Library</a>
+ * {@link android.support.v4.content.AsyncTaskLoader}
*/
+@Deprecated
public abstract class AsyncTaskLoader<D> extends Loader<D> {
static final String TAG = "AsyncTaskLoader";
static final boolean DEBUG = false;
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index 9323261f052c..94e1e2dff235 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -34,16 +34,18 @@ import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.URLSpan;
import android.util.Log;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.util.ArrayUtils;
+import libcore.io.IoUtils;
+
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
-import libcore.io.IoUtils;
/**
* Representation of a clipped data on the clipboard.
@@ -665,6 +667,25 @@ public class ClipData implements Parcelable {
b.append("NULL");
}
}
+
+ /** @hide */
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+
+ if (mHtmlText != null) {
+ proto.write(ClipDataProto.Item.HTML_TEXT, mHtmlText);
+ } else if (mText != null) {
+ proto.write(ClipDataProto.Item.TEXT, mText.toString());
+ } else if (mUri != null) {
+ proto.write(ClipDataProto.Item.URI, mUri.toString());
+ } else if (mIntent != null) {
+ mIntent.writeToProto(proto, ClipDataProto.Item.INTENT, true, true, true, true);
+ } else {
+ proto.write(ClipDataProto.Item.NOTHING, true);
+ }
+
+ proto.end(token);
+ }
}
/**
@@ -1048,6 +1069,26 @@ public class ClipData implements Parcelable {
}
/** @hide */
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+
+ if (mClipDescription != null) {
+ mClipDescription.writeToProto(proto, ClipDataProto.DESCRIPTION);
+ }
+ if (mIcon != null) {
+ final long iToken = proto.start(ClipDataProto.ICON);
+ proto.write(ClipDataProto.Icon.WIDTH, mIcon.getWidth());
+ proto.write(ClipDataProto.Icon.HEIGHT, mIcon.getHeight());
+ proto.end(iToken);
+ }
+ for (int i = 0; i < mItems.size(); i++) {
+ mItems.get(i).writeToProto(proto, ClipDataProto.ITEMS);
+ }
+
+ proto.end(token);
+ }
+
+ /** @hide */
public void collectUris(List<Uri> out) {
for (int i = 0; i < mItems.size(); ++i) {
ClipData.Item item = getItemAt(i);
diff --git a/core/java/android/content/ClipDescription.java b/core/java/android/content/ClipDescription.java
index 8e30fd6eb60f..19295fcf20f6 100644
--- a/core/java/android/content/ClipDescription.java
+++ b/core/java/android/content/ClipDescription.java
@@ -21,6 +21,7 @@ import android.os.Parcelable;
import android.os.PersistableBundle;
import android.text.TextUtils;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
@@ -337,6 +338,28 @@ public class ClipDescription implements Parcelable {
return !first;
}
+ /** @hide */
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+
+ final int size = mMimeTypes.size();
+ for (int i = 0; i < size; i++) {
+ proto.write(ClipDescriptionProto.MIME_TYPES, mMimeTypes.get(i));
+ }
+
+ if (mLabel != null) {
+ proto.write(ClipDescriptionProto.LABEL, mLabel.toString());
+ }
+ if (mExtras != null) {
+ mExtras.writeToProto(proto, ClipDescriptionProto.EXTRAS);
+ }
+ if (mTimeStamp > 0) {
+ proto.write(ClipDescriptionProto.TIMESTAMP_MS, mTimeStamp);
+ }
+
+ proto.end(token);
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java
index 718e465bf0de..73b6eb27bed3 100644
--- a/core/java/android/content/ClipboardManager.java
+++ b/core/java/android/content/ClipboardManager.java
@@ -16,13 +16,16 @@
package android.content;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemService;
import android.os.Handler;
-import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
+import com.android.internal.util.Preconditions;
+
import java.util.ArrayList;
/**
@@ -45,6 +48,7 @@ import java.util.ArrayList;
@SystemService(Context.CLIPBOARD_SERVICE)
public class ClipboardManager extends android.text.ClipboardManager {
private final Context mContext;
+ private final Handler mHandler;
private final IClipboard mService;
private final ArrayList<OnPrimaryClipChangedListener> mPrimaryClipChangedListeners
@@ -52,20 +56,11 @@ public class ClipboardManager extends android.text.ClipboardManager {
private final IOnPrimaryClipChangedListener.Stub mPrimaryClipChangedServiceListener
= new IOnPrimaryClipChangedListener.Stub() {
- public void dispatchPrimaryClipChanged() {
- mHandler.sendEmptyMessage(MSG_REPORT_PRIMARY_CLIP_CHANGED);
- }
- };
-
- static final int MSG_REPORT_PRIMARY_CLIP_CHANGED = 1;
-
- private final Handler mHandler = new Handler() {
@Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_REPORT_PRIMARY_CLIP_CHANGED:
- reportPrimaryClipChanged();
- }
+ public void dispatchPrimaryClipChanged() {
+ mHandler.post(() -> {
+ reportPrimaryClipChanged();
+ });
}
};
@@ -89,6 +84,7 @@ public class ClipboardManager extends android.text.ClipboardManager {
/** {@hide} */
public ClipboardManager(Context context, Handler handler) throws ServiceNotFoundException {
mContext = context;
+ mHandler = handler;
mService = IClipboard.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.CLIPBOARD_SERVICE));
}
@@ -98,12 +94,13 @@ public class ClipboardManager extends android.text.ClipboardManager {
* is involved in normal cut and paste operations.
*
* @param clip The clipped data item to set.
+ * @see #getPrimaryClip()
+ * @see #clearPrimaryClip()
*/
- public void setPrimaryClip(ClipData clip) {
+ public void setPrimaryClip(@NonNull ClipData clip) {
try {
- if (clip != null) {
- clip.prepareToLeaveProcess(true);
- }
+ Preconditions.checkNotNull(clip);
+ clip.prepareToLeaveProcess(true);
mService.setPrimaryClip(clip, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -111,9 +108,24 @@ public class ClipboardManager extends android.text.ClipboardManager {
}
/**
+ * Clears any current primary clip on the clipboard.
+ *
+ * @see #setPrimaryClip(ClipData)
+ */
+ public void clearPrimaryClip() {
+ try {
+ mService.clearPrimaryClip(mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns the current primary clip on the clipboard.
+ *
+ * @see #setPrimaryClip(ClipData)
*/
- public ClipData getPrimaryClip() {
+ public @Nullable ClipData getPrimaryClip() {
try {
return mService.getPrimaryClip(mContext.getOpPackageName());
} catch (RemoteException e) {
@@ -124,8 +136,10 @@ public class ClipboardManager extends android.text.ClipboardManager {
/**
* Returns a description of the current primary clip on the clipboard
* but not a copy of its data.
+ *
+ * @see #setPrimaryClip(ClipData)
*/
- public ClipDescription getPrimaryClipDescription() {
+ public @Nullable ClipDescription getPrimaryClipDescription() {
try {
return mService.getPrimaryClipDescription(mContext.getOpPackageName());
} catch (RemoteException e) {
diff --git a/core/java/android/content/ComponentName.java b/core/java/android/content/ComponentName.java
index ea6b7690b431..fc5853353ce6 100644
--- a/core/java/android/content/ComponentName.java
+++ b/core/java/android/content/ComponentName.java
@@ -21,9 +21,9 @@ import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import android.util.proto.ProtoOutputStream;
import java.io.PrintWriter;
-import java.lang.Comparable;
/**
* Identifier for a specific application component
@@ -33,7 +33,7 @@ import java.lang.Comparable;
* pieces of information, encapsulated here, are required to identify
* a component: the package (a String) it exists in, and the class (a String)
* name inside of that package.
- *
+ *
*/
public final class ComponentName implements Parcelable, Cloneable, Comparable<ComponentName> {
private final String mPackage;
@@ -91,7 +91,7 @@ public final class ComponentName implements Parcelable, Cloneable, Comparable<Co
/**
* Create a new component identifier.
- *
+ *
* @param pkg The name of the package that the component exists in. Can
* not be null.
* @param cls The name of the class inside of <var>pkg</var> that
@@ -106,7 +106,7 @@ public final class ComponentName implements Parcelable, Cloneable, Comparable<Co
/**
* Create a new component identifier from a Context and class name.
- *
+ *
* @param pkg A Context for the package implementing the component,
* from which the actual package name will be retrieved.
* @param cls The name of the class inside of <var>pkg</var> that
@@ -120,7 +120,7 @@ public final class ComponentName implements Parcelable, Cloneable, Comparable<Co
/**
* Create a new component identifier from a Context and Class object.
- *
+ *
* @param pkg A Context for the package implementing the component, from
* which the actual package name will be retrieved.
* @param cls The Class object of the desired component, from which the
@@ -141,14 +141,14 @@ public final class ComponentName implements Parcelable, Cloneable, Comparable<Co
public @NonNull String getPackageName() {
return mPackage;
}
-
+
/**
* Return the class name of this component.
*/
public @NonNull String getClassName() {
return mClass;
}
-
+
/**
* Return the class name, either fully qualified or in a shortened form
* (with a leading '.') if it is a suffix of the package.
@@ -163,7 +163,7 @@ public final class ComponentName implements Parcelable, Cloneable, Comparable<Co
}
return mClass;
}
-
+
private static void appendShortClassName(StringBuilder sb, String packageName,
String className) {
if (className.startsWith(packageName)) {
@@ -195,26 +195,26 @@ public final class ComponentName implements Parcelable, Cloneable, Comparable<Co
* class names contained in the ComponentName. You can later recover
* the ComponentName from this string through
* {@link #unflattenFromString(String)}.
- *
+ *
* @return Returns a new String holding the package and class names. This
* is represented as the package name, concatenated with a '/' and then the
* class name.
- *
+ *
* @see #unflattenFromString(String)
*/
public @NonNull String flattenToString() {
return mPackage + "/" + mClass;
}
-
+
/**
* The same as {@link #flattenToString()}, but abbreviates the class
* name if it is a suffix of the package. The result can still be used
* with {@link #unflattenFromString(String)}.
- *
+ *
* @return Returns a new String holding the package and class names. This
* is represented as the package name, concatenated with a '/' and then the
* class name.
- *
+ *
* @see #unflattenFromString(String)
*/
public @NonNull String flattenToShortString() {
@@ -250,11 +250,11 @@ public final class ComponentName implements Parcelable, Cloneable, Comparable<Co
* followed by a '.' then the final class name will be the concatenation
* of the package name with the string following the '/'. Thus
* "com.foo/.Blah" becomes package="com.foo" class="com.foo.Blah".
- *
+ *
* @param str The String that was returned by flattenToString().
* @return Returns a new ComponentName containing the package and class
* names that were encoded in <var>str</var>
- *
+ *
* @see #flattenToString()
*/
public static @Nullable ComponentName unflattenFromString(@NonNull String str) {
@@ -269,7 +269,7 @@ public final class ComponentName implements Parcelable, Cloneable, Comparable<Co
}
return new ComponentName(pkg, cls);
}
-
+
/**
* Return string representation of this class without the class's name
* as a prefix.
@@ -283,6 +283,14 @@ public final class ComponentName implements Parcelable, Cloneable, Comparable<Co
return "ComponentInfo{" + mPackage + "/" + mClass + "}";
}
+ /** Put this here so that individual services don't have to reimplement this. @hide */
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(ComponentNameProto.PACKAGE_NAME, mPackage);
+ proto.write(ComponentNameProto.CLASS_NAME, mClass);
+ proto.end(token);
+ }
+
@Override
public boolean equals(Object obj) {
try {
@@ -311,7 +319,7 @@ public final class ComponentName implements Parcelable, Cloneable, Comparable<Co
}
return this.mClass.compareTo(that.mClass);
}
-
+
public int describeContents() {
return 0;
}
@@ -324,10 +332,10 @@ public final class ComponentName implements Parcelable, Cloneable, Comparable<Co
/**
* Write a ComponentName to a Parcel, handling null pointers. Must be
* read with {@link #readFromParcel(Parcel)}.
- *
+ *
* @param c The ComponentName to be written.
* @param out The Parcel in which the ComponentName will be placed.
- *
+ *
* @see #readFromParcel(Parcel)
*/
public static void writeToParcel(ComponentName c, Parcel out) {
@@ -337,23 +345,23 @@ public final class ComponentName implements Parcelable, Cloneable, Comparable<Co
out.writeString(null);
}
}
-
+
/**
* Read a ComponentName from a Parcel that was previously written
* with {@link #writeToParcel(ComponentName, Parcel)}, returning either
* a null or new object as appropriate.
- *
+ *
* @param in The Parcel from which to read the ComponentName
* @return Returns a new ComponentName matching the previously written
* object, or null if a null had been written.
- *
+ *
* @see #writeToParcel(ComponentName, Parcel)
*/
public static ComponentName readFromParcel(Parcel in) {
String pkg = in.readString();
return pkg != null ? new ComponentName(pkg, in) : null;
}
-
+
public static final Parcelable.Creator<ComponentName> CREATOR
= new Parcelable.Creator<ComponentName>() {
public ComponentName createFromParcel(Parcel in) {
@@ -371,7 +379,7 @@ public final class ComponentName implements Parcelable, Cloneable, Comparable<Co
* must not use this with data written by
* {@link #writeToParcel(ComponentName, Parcel)} since it is not possible
* to handle a null ComponentObject here.
- *
+ *
* @param in The Parcel containing the previously written ComponentName,
* positioned at the location in the buffer where it was written.
*/
@@ -388,4 +396,14 @@ public final class ComponentName implements Parcelable, Cloneable, Comparable<Co
mPackage = pkg;
mClass = in.readString();
}
+
+ /**
+ * Interface for classes associated with a component name.
+ * @hide
+ */
+ @FunctionalInterface
+ public interface WithComponentName {
+ /** Return the associated component name. */
+ ComponentName getComponentName();
+ }
}
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index f8c139feaae3..2d490a03bd76 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -22,6 +22,7 @@ import android.content.res.AssetFileDescriptor;
import android.database.CrossProcessCursorWrapper;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Binder;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.DeadObjectException;
@@ -102,8 +103,16 @@ public class ContentProviderClient implements AutoCloseable {
if (sAnrHandler == null) {
sAnrHandler = new Handler(Looper.getMainLooper(), null, true /* async */);
}
+
+ // If the remote process hangs, we're going to kill it, so we're
+ // technically okay doing blocking calls.
+ Binder.allowBlocking(mContentProvider.asBinder());
} else {
mAnrRunnable = null;
+
+ // If we're no longer watching for hangs, revert back to default
+ // blocking behavior.
+ Binder.defaultBlocking(mContentProvider.asBinder());
}
}
}
@@ -511,6 +520,10 @@ public class ContentProviderClient implements AutoCloseable {
private boolean closeInternal() {
mCloseGuard.close();
if (mClosed.compareAndSet(false, true)) {
+ // We can't do ANR checks after we cease to exist! Reset any
+ // blocking behavior changes we might have made.
+ setDetectNotResponding(0);
+
if (mStable) {
return mContentResolver.releaseProvider(mContentProvider);
} else {
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 9ccc552f77f5..32a674363535 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -51,7 +51,6 @@ import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.MimeIconUtils;
import com.android.internal.util.Preconditions;
@@ -166,6 +165,15 @@ public abstract class ContentResolver {
public static final String SYNC_EXTRAS_DISALLOW_METERED = "allow_metered";
/**
+ * {@hide} Integer extra containing a SyncExemption flag.
+ *
+ * Only the system and the shell user can set it.
+ *
+ * This extra is "virtual". Once passed to the system server, it'll be removed from the bundle.
+ */
+ public static final String SYNC_VIRTUAL_EXTRAS_EXEMPTION_FLAG = "v_exemption";
+
+ /**
* Set by the SyncManager to request that the SyncAdapter initialize itself for
* the given account/authority pair. One required initialization step is to
* ensure that {@link #setIsSyncable(android.accounts.Account, String, int)} has been
@@ -335,7 +343,7 @@ public abstract class ContentResolver {
public static final String EXTRA_HONORED_ARGS = "android.content.extra.HONORED_ARGS";
/** @hide */
- @IntDef(flag = false, value = {
+ @IntDef(flag = false, prefix = { "QUERY_SORT_DIRECTION_" }, value = {
QUERY_SORT_DIRECTION_ASCENDING,
QUERY_SORT_DIRECTION_DESCENDING
})
@@ -482,11 +490,10 @@ public abstract class ContentResolver {
public static final int SYNC_OBSERVER_TYPE_ALL = 0x7fffffff;
/** @hide */
- @IntDef(flag = true,
- value = {
- NOTIFY_SYNC_TO_NETWORK,
- NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS
- })
+ @IntDef(flag = true, prefix = { "NOTIFY_" }, value = {
+ NOTIFY_SYNC_TO_NETWORK,
+ NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface NotifyFlags {}
@@ -506,6 +513,47 @@ public abstract class ContentResolver {
*/
public static final int NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS = 1<<1;
+ /**
+ * No exception, throttled by app standby normally.
+ * @hide
+ */
+ public static final int SYNC_EXEMPTION_NONE = 0;
+
+ /**
+ * Exemption given to a sync request made by a foreground app (including
+ * PROCESS_STATE_IMPORTANT_FOREGROUND).
+ *
+ * At the schedule time, we promote the sync adapter app for a higher bucket:
+ * - If the device is not dozing (so the sync will start right away)
+ * promote to ACTIVE for 1 hour.
+ * - If the device is dozing (so the sync *won't* start right away),
+ * promote to WORKING_SET for 4 hours, so it'll get a higher chance to be started once the
+ * device comes out of doze.
+ * - When the sync actually starts, we promote the sync adapter app to ACTIVE for 10 minutes,
+ * so it can schedule and start more syncs without getting throttled, even when the first
+ * operation was canceled and now we're retrying.
+ *
+ *
+ * @hide
+ */
+ public static final int SYNC_EXEMPTION_PROMOTE_BUCKET = 1;
+
+ /**
+ * In addition to {@link #SYNC_EXEMPTION_PROMOTE_BUCKET}, we put the sync adapter app in the
+ * temp whitelist for 10 minutes, so that even RARE apps can run syncs right away.
+ * @hide
+ */
+ public static final int SYNC_EXEMPTION_PROMOTE_BUCKET_WITH_TEMP = 2;
+
+ /** @hide */
+ @IntDef(flag = false, prefix = { "SYNC_EXEMPTION_" }, value = {
+ SYNC_EXEMPTION_NONE,
+ SYNC_EXEMPTION_PROMOTE_BUCKET,
+ SYNC_EXEMPTION_PROMOTE_BUCKET_WITH_TEMP,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SyncExemption {}
+
// Always log queries which take 500ms+; shorter queries are
// sampled accordingly.
private static final boolean ENABLE_CONTENT_SAMPLE = false;
@@ -562,6 +610,8 @@ public abstract class ContentResolver {
try {
return provider.getType(url);
} catch (RemoteException e) {
+ // Arbitrary and not worth documenting, as Activity
+ // Manager will kill this process shortly anyway.
return null;
} catch (java.lang.Exception e) {
Log.w(TAG, "Failed to get type for: " + url + " (" + e.getMessage() + ")");
@@ -580,9 +630,7 @@ public abstract class ContentResolver {
ContentProvider.getUriWithoutUserId(url), resolveUserId(url));
return type;
} catch (RemoteException e) {
- // Arbitrary and not worth documenting, as Activity
- // Manager will kill this process shortly anyway.
- return null;
+ throw e.rethrowFromSystemServer();
} catch (java.lang.Exception e) {
Log.w(TAG, "Failed to get type for: " + url + " (" + e.getMessage() + ")");
return null;
@@ -1924,6 +1972,7 @@ public abstract class ContentResolver {
getContentService().registerContentObserver(uri, notifyForDescendents,
observer.getContentObserver(), userHandle, mTargetSdkVersion);
} catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -1942,6 +1991,7 @@ public abstract class ContentResolver {
contentObserver);
}
} catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -2049,6 +2099,7 @@ public abstract class ContentResolver {
syncToNetwork ? NOTIFY_SYNC_TO_NETWORK : 0,
userHandle, mTargetSdkVersion);
} catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -2065,6 +2116,7 @@ public abstract class ContentResolver {
observer != null && observer.deliverSelfNotifications(), flags,
userHandle, mTargetSdkVersion);
} catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -2083,8 +2135,26 @@ public abstract class ContentResolver {
Preconditions.checkNotNull(uri, "uri");
try {
ActivityManager.getService().takePersistableUriPermission(
- ContentProvider.getUriWithoutUserId(uri), modeFlags, resolveUserId(uri));
+ ContentProvider.getUriWithoutUserId(uri), modeFlags, /* toPackage= */ null,
+ resolveUserId(uri));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void takePersistableUriPermission(@NonNull String toPackage, @NonNull Uri uri,
+ @Intent.AccessUriMode int modeFlags) {
+ Preconditions.checkNotNull(toPackage, "toPackage");
+ Preconditions.checkNotNull(uri, "uri");
+ try {
+ ActivityManager.getService().takePersistableUriPermission(
+ ContentProvider.getUriWithoutUserId(uri), modeFlags, toPackage,
+ resolveUserId(uri));
} catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -2101,8 +2171,10 @@ public abstract class ContentResolver {
Preconditions.checkNotNull(uri, "uri");
try {
ActivityManager.getService().releasePersistableUriPermission(
- ContentProvider.getUriWithoutUserId(uri), modeFlags, resolveUserId(uri));
+ ContentProvider.getUriWithoutUserId(uri), modeFlags, /* toPackage= */ null,
+ resolveUserId(uri));
} catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -2121,7 +2193,7 @@ public abstract class ContentResolver {
return ActivityManager.getService()
.getPersistedUriPermissions(mPackageName, true).getList();
} catch (RemoteException e) {
- throw new RuntimeException("Activity manager has died", e);
+ throw e.rethrowFromSystemServer();
}
}
@@ -2137,7 +2209,7 @@ public abstract class ContentResolver {
return ActivityManager.getService()
.getPersistedUriPermissions(mPackageName, false).getList();
} catch (RemoteException e) {
- throw new RuntimeException("Activity manager has died", e);
+ throw e.rethrowFromSystemServer();
}
}
@@ -2216,7 +2288,7 @@ public abstract class ContentResolver {
try {
getContentService().syncAsUser(request, userId);
} catch(RemoteException e) {
- // Shouldn't happen.
+ throw e.rethrowFromSystemServer();
}
}
@@ -2228,7 +2300,7 @@ public abstract class ContentResolver {
try {
getContentService().sync(request);
} catch(RemoteException e) {
- // Shouldn't happen.
+ throw e.rethrowFromSystemServer();
}
}
@@ -2292,6 +2364,7 @@ public abstract class ContentResolver {
try {
getContentService().cancelSync(account, authority, null);
} catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -2303,6 +2376,7 @@ public abstract class ContentResolver {
try {
getContentService().cancelSyncAsUser(account, authority, null, userId);
} catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -2314,7 +2388,7 @@ public abstract class ContentResolver {
try {
return getContentService().getSyncAdapterTypes();
} catch (RemoteException e) {
- throw new RuntimeException("the ContentService should always be reachable", e);
+ throw e.rethrowFromSystemServer();
}
}
@@ -2326,7 +2400,7 @@ public abstract class ContentResolver {
try {
return getContentService().getSyncAdapterTypesAsUser(userId);
} catch (RemoteException e) {
- throw new RuntimeException("the ContentService should always be reachable", e);
+ throw e.rethrowFromSystemServer();
}
}
@@ -2340,8 +2414,8 @@ public abstract class ContentResolver {
try {
return getContentService().getSyncAdapterPackagesForAuthorityAsUser(authority, userId);
} catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
- return ArrayUtils.emptyArray(String.class);
}
/**
@@ -2357,7 +2431,7 @@ public abstract class ContentResolver {
try {
return getContentService().getSyncAutomatically(account, authority);
} catch (RemoteException e) {
- throw new RuntimeException("the ContentService should always be reachable", e);
+ throw e.rethrowFromSystemServer();
}
}
@@ -2370,7 +2444,7 @@ public abstract class ContentResolver {
try {
return getContentService().getSyncAutomaticallyAsUser(account, authority, userId);
} catch (RemoteException e) {
- throw new RuntimeException("the ContentService should always be reachable", e);
+ throw e.rethrowFromSystemServer();
}
}
@@ -2396,8 +2470,7 @@ public abstract class ContentResolver {
try {
getContentService().setSyncAutomaticallyAsUser(account, authority, sync, userId);
} catch (RemoteException e) {
- // exception ignored; if this is thrown then it means the runtime is in the midst of
- // being restarted
+ throw e.rethrowFromSystemServer();
}
}
@@ -2428,28 +2501,22 @@ public abstract class ContentResolver {
* @param account the account to specify in the sync
* @param authority the provider to specify in the sync request
* @param extras extra parameters to go along with the sync request
- * @param pollFrequency how frequently the sync should be performed, in seconds. A minimum value
- * of 1 hour is enforced.
+ * @param pollFrequency how frequently the sync should be performed, in seconds.
+ * On Android API level 24 and above, a minmam interval of 15 minutes is enforced.
+ * On previous versions, the minimum interval is 1 hour.
* @throws IllegalArgumentException if an illegal extra was set or if any of the parameters
* are null.
*/
public static void addPeriodicSync(Account account, String authority, Bundle extras,
long pollFrequency) {
validateSyncExtrasBundle(extras);
- if (extras.getBoolean(SYNC_EXTRAS_MANUAL, false)
- || extras.getBoolean(SYNC_EXTRAS_DO_NOT_RETRY, false)
- || extras.getBoolean(SYNC_EXTRAS_IGNORE_BACKOFF, false)
- || extras.getBoolean(SYNC_EXTRAS_IGNORE_SETTINGS, false)
- || extras.getBoolean(SYNC_EXTRAS_INITIALIZE, false)
- || extras.getBoolean(SYNC_EXTRAS_FORCE, false)
- || extras.getBoolean(SYNC_EXTRAS_EXPEDITED, false)) {
+ if (invalidPeriodicExtras(extras)) {
throw new IllegalArgumentException("illegal extras were set");
}
try {
getContentService().addPeriodicSync(account, authority, extras, pollFrequency);
} catch (RemoteException e) {
- // exception ignored; if this is thrown then it means the runtime is in the midst of
- // being restarted
+ throw e.rethrowFromSystemServer();
}
}
@@ -2488,7 +2555,7 @@ public abstract class ContentResolver {
try {
getContentService().removePeriodicSync(account, authority, extras);
} catch (RemoteException e) {
- throw new RuntimeException("the ContentService should always be reachable", e);
+ throw e.rethrowFromSystemServer();
}
}
@@ -2512,8 +2579,7 @@ public abstract class ContentResolver {
try {
getContentService().cancelRequest(request);
} catch (RemoteException e) {
- // exception ignored; if this is thrown then it means the runtime is in the midst of
- // being restarted
+ throw e.rethrowFromSystemServer();
}
}
@@ -2530,7 +2596,7 @@ public abstract class ContentResolver {
try {
return getContentService().getPeriodicSyncs(account, authority, null);
} catch (RemoteException e) {
- throw new RuntimeException("the ContentService should always be reachable", e);
+ throw e.rethrowFromSystemServer();
}
}
@@ -2544,7 +2610,7 @@ public abstract class ContentResolver {
try {
return getContentService().getIsSyncable(account, authority);
} catch (RemoteException e) {
- throw new RuntimeException("the ContentService should always be reachable", e);
+ throw e.rethrowFromSystemServer();
}
}
@@ -2557,7 +2623,7 @@ public abstract class ContentResolver {
try {
return getContentService().getIsSyncableAsUser(account, authority, userId);
} catch (RemoteException e) {
- throw new RuntimeException("the ContentService should always be reachable", e);
+ throw e.rethrowFromSystemServer();
}
}
@@ -2571,8 +2637,7 @@ public abstract class ContentResolver {
try {
getContentService().setIsSyncable(account, authority, syncable);
} catch (RemoteException e) {
- // exception ignored; if this is thrown then it means the runtime is in the midst of
- // being restarted
+ throw e.rethrowFromSystemServer();
}
}
@@ -2588,7 +2653,7 @@ public abstract class ContentResolver {
try {
return getContentService().getMasterSyncAutomatically();
} catch (RemoteException e) {
- throw new RuntimeException("the ContentService should always be reachable", e);
+ throw e.rethrowFromSystemServer();
}
}
@@ -2600,7 +2665,7 @@ public abstract class ContentResolver {
try {
return getContentService().getMasterSyncAutomaticallyAsUser(userId);
} catch (RemoteException e) {
- throw new RuntimeException("the ContentService should always be reachable", e);
+ throw e.rethrowFromSystemServer();
}
}
@@ -2624,8 +2689,7 @@ public abstract class ContentResolver {
try {
getContentService().setMasterSyncAutomaticallyAsUser(sync, userId);
} catch (RemoteException e) {
- // exception ignored; if this is thrown then it means the runtime is in the midst of
- // being restarted
+ throw e.rethrowFromSystemServer();
}
}
@@ -2649,7 +2713,7 @@ public abstract class ContentResolver {
try {
return getContentService().isSyncActive(account, authority, null);
} catch (RemoteException e) {
- throw new RuntimeException("the ContentService should always be reachable", e);
+ throw e.rethrowFromSystemServer();
}
}
@@ -2675,7 +2739,7 @@ public abstract class ContentResolver {
}
return syncs.get(0);
} catch (RemoteException e) {
- throw new RuntimeException("the ContentService should always be reachable", e);
+ throw e.rethrowFromSystemServer();
}
}
@@ -2692,7 +2756,7 @@ public abstract class ContentResolver {
try {
return getContentService().getCurrentSyncs();
} catch (RemoteException e) {
- throw new RuntimeException("the ContentService should always be reachable", e);
+ throw e.rethrowFromSystemServer();
}
}
@@ -2704,7 +2768,7 @@ public abstract class ContentResolver {
try {
return getContentService().getCurrentSyncsAsUser(userId);
} catch (RemoteException e) {
- throw new RuntimeException("the ContentService should always be reachable", e);
+ throw e.rethrowFromSystemServer();
}
}
@@ -2719,7 +2783,7 @@ public abstract class ContentResolver {
try {
return getContentService().getSyncStatus(account, authority, null);
} catch (RemoteException e) {
- throw new RuntimeException("the ContentService should always be reachable", e);
+ throw e.rethrowFromSystemServer();
}
}
@@ -2732,7 +2796,7 @@ public abstract class ContentResolver {
try {
return getContentService().getSyncStatusAsUser(account, authority, null, userId);
} catch (RemoteException e) {
- throw new RuntimeException("the ContentService should always be reachable", e);
+ throw e.rethrowFromSystemServer();
}
}
@@ -2757,7 +2821,7 @@ public abstract class ContentResolver {
try {
return getContentService().isSyncPendingAsUser(account, authority, null, userId);
} catch (RemoteException e) {
- throw new RuntimeException("the ContentService should always be reachable", e);
+ throw e.rethrowFromSystemServer();
}
}
@@ -2789,7 +2853,7 @@ public abstract class ContentResolver {
getContentService().addStatusChangeListener(mask, observer);
return observer;
} catch (RemoteException e) {
- throw new RuntimeException("the ContentService should always be reachable", e);
+ throw e.rethrowFromSystemServer();
}
}
@@ -2804,8 +2868,7 @@ public abstract class ContentResolver {
try {
getContentService().removeStatusChangeListener((ISyncStatusObserver.Stub) handle);
} catch (RemoteException e) {
- // exception ignored; if this is thrown then it means the runtime is in the midst of
- // being restarted
+ throw e.rethrowFromSystemServer();
}
}
@@ -2975,9 +3038,7 @@ public abstract class ContentResolver {
return sContentService;
}
IBinder b = ServiceManager.getService(CONTENT_SERVICE_NAME);
- if (false) Log.v("ContentService", "default service binder = " + b);
sContentService = IContentService.Stub.asInterface(b);
- if (false) Log.v("ContentService", "default service = " + sContentService);
return sContentService;
}
@@ -2986,7 +3047,7 @@ public abstract class ContentResolver {
return mPackageName;
}
- private static IContentService sContentService;
+ private static volatile IContentService sContentService;
private final Context mContext;
final String mPackageName;
@@ -3000,6 +3061,11 @@ public abstract class ContentResolver {
}
/** @hide */
+ public int getUserId() {
+ return mContext.getUserId();
+ }
+
+ /** @hide */
public Drawable getTypeDrawable(String mimeType) {
return MimeIconUtils.loadMimeIcon(mContext, mimeType);
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 9c4767212c6c..97d520717c70 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -32,6 +32,7 @@ import android.annotation.StyleableRes;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UserIdInt;
+import android.app.ActivityManager;
import android.app.IApplicationThread;
import android.app.IServiceConnection;
import android.app.VrManager;
@@ -48,9 +49,11 @@ import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.Looper;
import android.os.StatFs;
@@ -75,6 +78,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
/**
* Interface to global information about an application environment. This is
@@ -219,17 +223,16 @@ public abstract class Context {
public static final int MODE_NO_LOCALIZED_COLLATORS = 0x0010;
/** @hide */
- @IntDef(flag = true,
- value = {
- BIND_AUTO_CREATE,
- BIND_DEBUG_UNBIND,
- BIND_NOT_FOREGROUND,
- BIND_ABOVE_CLIENT,
- BIND_ALLOW_OOM_MANAGEMENT,
- BIND_WAIVE_PRIORITY,
- BIND_IMPORTANT,
- BIND_ADJUST_WITH_ACTIVITY
- })
+ @IntDef(flag = true, prefix = { "BIND_" }, value = {
+ BIND_AUTO_CREATE,
+ BIND_DEBUG_UNBIND,
+ BIND_NOT_FOREGROUND,
+ BIND_ABOVE_CLIENT,
+ BIND_ALLOW_OOM_MANAGEMENT,
+ BIND_WAIVE_PRIORITY,
+ BIND_IMPORTANT,
+ BIND_ADJUST_WITH_ACTIVITY
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface BindServiceFlags {}
@@ -323,6 +326,15 @@ public abstract class Context {
public static final int BIND_ADJUST_WITH_ACTIVITY = 0x0080;
/**
+ * @hide Flag for {@link #bindService}: allows binding to a service provided
+ * by an instant app. Note that the caller may not have access to the instant
+ * app providing the service which is a violation of the instant app sandbox.
+ * This flag is intended ONLY for development/testing and should be used with
+ * great care. Only the system is allowed to use this flag.
+ */
+ public static final int BIND_ALLOW_INSTANT = 0x00400000;
+
+ /**
* @hide Flag for {@link #bindService}: like {@link #BIND_NOT_FOREGROUND}, but puts it
* up in to the important background state (instead of transient).
*/
@@ -404,10 +416,9 @@ public abstract class Context {
public static final int BIND_EXTERNAL_SERVICE = 0x80000000;
/** @hide */
- @IntDef(flag = true,
- value = {
- RECEIVER_VISIBLE_TO_INSTANT_APPS
- })
+ @IntDef(flag = true, prefix = { "RECEIVER_VISIBLE_" }, value = {
+ RECEIVER_VISIBLE_TO_INSTANT_APPS
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface RegisterReceiverFlags {}
@@ -462,6 +473,16 @@ public abstract class Context {
public abstract Looper getMainLooper();
/**
+ * Return an {@link Executor} that will run enqueued tasks on the main
+ * thread associated with this context. This is the thread used to dispatch
+ * calls to application components (activities, services, etc).
+ */
+ public Executor getMainExecutor() {
+ // This is pretty inefficient, which is why ContextImpl overrides it
+ return new HandlerExecutor(new Handler(getMainLooper()));
+ }
+
+ /**
* Return the context of the single, global Application object of the
* current process. This generally should only be used if you need a
* Context whose lifecycle is separate from the current context, that is
@@ -539,6 +560,7 @@ public abstract class Context {
*
* @param resId Resource id for the CharSequence text
*/
+ @NonNull
public final CharSequence getText(@StringRes int resId) {
return getResources().getText(resId);
}
@@ -595,8 +617,7 @@ public abstract class Context {
* @param id The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
- * @return An object that can be used to draw this resource, or
- * {@code null} if the resource could not be resolved.
+ * @return An object that can be used to draw this resource.
* @throws android.content.res.Resources.NotFoundException if the given ID
* does not exist.
*/
@@ -612,12 +633,11 @@ public abstract class Context {
* @param id The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
- * @return A color state list, or {@code null} if the resource could not be
- * resolved.
+ * @return A color state list.
* @throws android.content.res.Resources.NotFoundException if the given ID
* does not exist.
*/
- @Nullable
+ @NonNull
public final ColorStateList getColorStateList(@ColorRes int id) {
return getResources().getColorStateList(id, getTheme());
}
@@ -754,6 +774,8 @@ public abstract class Context {
* to any callers for the same name, meaning they will see each other's
* edits as soon as they are made.
*
+ * This method is thead-safe.
+ *
* @param name Desired preferences file. If a preferences file by this name
* does not exist, it will be created when you retrieve an
* editor (SharedPreferences.edit()) and then commit changes (Editor.commit()).
@@ -1813,13 +1835,17 @@ public abstract class Context {
* See {@link android.content.Context#startActivity(Intent, Bundle)}
* Context.startActivity(Intent, Bundle)} for more details.
*
+ * @return The corresponding flag {@link ActivityManager#START_CANCELED},
+ * {@link ActivityManager#START_SUCCESS} etc. indicating whether the launch was
+ * successful.
+ *
* @throws ActivityNotFoundException &nbsp;
*
* @see #startActivities(Intent[])
* @see PackageManager#resolveActivity
*/
@RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
- public void startActivitiesAsUser(Intent[] intents, Bundle options, UserHandle userHandle) {
+ public int startActivitiesAsUser(Intent[] intents, Bundle options, UserHandle userHandle) {
throw new RuntimeException("Not implemented. Must override in a subclass.");
}
@@ -2826,10 +2852,17 @@ public abstract class Context {
* example, if this Context is an Activity that is stopped, the service will
* not be required to continue running until the Activity is resumed.
*
- * <p>This function will throw {@link SecurityException} if you do not
+ * <p>If the service does not support binding, it may return {@code null} from
+ * its {@link android.app.Service#onBind(Intent) onBind()} method. If it does, then
+ * the ServiceConnection's
+ * {@link ServiceConnection#onNullBinding(ComponentName) onNullBinding()} method
+ * will be invoked instead of
+ * {@link ServiceConnection#onServiceConnected(ComponentName, IBinder) onServiceConnected()}.
+ *
+ * <p>This method will throw {@link SecurityException} if the calling app does not
* have permission to bind to the given service.
*
- * <p class="note">Note: this method <em>can not be called from a
+ * <p class="note">Note: this method <em>cannot be called from a
* {@link BroadcastReceiver} component</em>. A pattern you can use to
* communicate from a BroadcastReceiver to a Service is to call
* {@link #startService} with the arguments containing the command to be
@@ -2850,10 +2883,12 @@ public abstract class Context {
* {@link #BIND_NOT_FOREGROUND}, {@link #BIND_ABOVE_CLIENT},
* {@link #BIND_ALLOW_OOM_MANAGEMENT}, or
* {@link #BIND_WAIVE_PRIORITY}.
- * @return If you have successfully bound to the service, {@code true} is returned;
- * {@code false} is returned if the connection is not made so you will not
- * receive the service object. However, you should still call
- * {@link #unbindService} to release the connection.
+ * @return {@code true} if the system is in the process of bringing up a
+ * service that your client has permission to bind to; {@code false}
+ * if the system couldn't find the service or if your client doesn't
+ * have permission to bind to it. If this value is {@code true}, you
+ * should later call {@link #unbindService} to release the
+ * connection.
*
* @throws SecurityException If the caller does not have permission to access the service
* or the service can not be found.
@@ -2931,7 +2966,7 @@ public abstract class Context {
@Nullable String profileFile, @Nullable Bundle arguments);
/** @hide */
- @StringDef({
+ @StringDef(suffix = { "_SERVICE" }, value = {
POWER_SERVICE,
WINDOW_SERVICE,
LAYOUT_INFLATER_SERVICE,
@@ -2965,7 +3000,7 @@ public abstract class Context {
//@hide: LOWPAN_SERVICE,
//@hide: WIFI_RTT_SERVICE,
//@hide: ETHERNET_SERVICE,
- WIFI_RTT_SERVICE,
+ WIFI_RTT_RANGING_SERVICE,
NSD_SERVICE,
AUDIO_SERVICE,
FINGERPRINT_SERVICE,
@@ -3019,7 +3054,10 @@ public abstract class Context {
//@hide: CONTEXTHUB_SERVICE,
SYSTEM_HEALTH_SERVICE,
//@hide: INCIDENT_SERVICE,
+ //@hide: STATS_COMPANION_SERVICE,
COMPANION_DEVICE_SERVICE,
+ CROSS_PROFILE_APPS_SERVICE,
+ //@hide: SYSTEM_UPDATE_SERVICE,
//@hide: TIME_DETECTOR_SERVICE,
//@hide: TIME_ZONE_DETECTOR_SERVICE,
})
@@ -3100,6 +3138,14 @@ public abstract class Context {
* service objects between various different contexts (Activities, Applications,
* Services, Providers, etc.)
*
+ * <p>Note: Instant apps, for which {@link PackageManager#isInstantApp()} returns true,
+ * don't have access to the following system services: {@link #DEVICE_POLICY_SERVICE},
+ * {@link #FINGERPRINT_SERVICE}, {@link #SHORTCUT_SERVICE}, {@link #USB_SERVICE},
+ * {@link #WALLPAPER_SERVICE}, {@link #WIFI_P2P_SERVICE}, {@link #WIFI_SERVICE},
+ * {@link #WIFI_AWARE_SERVICE}. For these services this method will return <code>null</code>.
+ * Generally, if you are running as an instant app you should always check whether the result
+ * of this method is null.
+ *
* @param name The name of the desired service.
*
* @return The service or null if the name does not exist.
@@ -3183,6 +3229,14 @@ public abstract class Context {
* Services, Providers, etc.)
* </p>
*
+ * <p>Note: Instant apps, for which {@link PackageManager#isInstantApp()} returns true,
+ * don't have access to the following system services: {@link #DEVICE_POLICY_SERVICE},
+ * {@link #FINGERPRINT_SERVICE}, {@link #SHORTCUT_SERVICE}, {@link #USB_SERVICE},
+ * {@link #WALLPAPER_SERVICE}, {@link #WIFI_P2P_SERVICE}, {@link #WIFI_SERVICE},
+ * {@link #WIFI_AWARE_SERVICE}. For these services this method will return <code>null</code>.
+ * Generally, if you are running as an instant app you should always check whether the result
+ * of this method is null.
+ *
* @param serviceClass The class of the desired service.
* @return The service or null if the class is not a supported system service.
*/
@@ -3204,7 +3258,7 @@ public abstract class Context {
public abstract @Nullable String getSystemServiceName(@NonNull Class<?> serviceClass);
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.os.PowerManager} for controlling power management,
* including "wake locks," which let you keep the device on while
* you're running long tasks.
@@ -3212,117 +3266,128 @@ public abstract class Context {
public static final String POWER_SERVICE = "power";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.os.RecoverySystem} for accessing the recovery system
* service.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @hide
*/
public static final String RECOVERY_SERVICE = "recovery";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.os.SystemUpdateManager} for accessing the system update
+ * manager service.
+ *
+ * @see #getSystemService(String)
+ * @hide
+ */
+ @SystemApi
+ public static final String SYSTEM_UPDATE_SERVICE = "system_update";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.view.WindowManager} for accessing the system's window
* manager.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.view.WindowManager
*/
public static final String WINDOW_SERVICE = "window";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.view.LayoutInflater} for inflating layout resources in this
* context.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.view.LayoutInflater
*/
public static final String LAYOUT_INFLATER_SERVICE = "layout_inflater";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.accounts.AccountManager} for receiving intents at a
* time of your choosing.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.accounts.AccountManager
*/
public static final String ACCOUNT_SERVICE = "account";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.app.ActivityManager} for interacting with the global
* system state.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.app.ActivityManager
*/
public static final String ACTIVITY_SERVICE = "activity";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.app.AlarmManager} for receiving intents at a
* time of your choosing.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.app.AlarmManager
*/
public static final String ALARM_SERVICE = "alarm";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.app.NotificationManager} for informing the user of
* background events.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.app.NotificationManager
*/
public static final String NOTIFICATION_SERVICE = "notification";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.view.accessibility.AccessibilityManager} for giving the user
* feedback for UI events through the registered event listeners.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.view.accessibility.AccessibilityManager
*/
public static final String ACCESSIBILITY_SERVICE = "accessibility";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.view.accessibility.CaptioningManager} for obtaining
* captioning properties and listening for changes in captioning
* preferences.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.view.accessibility.CaptioningManager
*/
public static final String CAPTIONING_SERVICE = "captioning";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.app.NotificationManager} for controlling keyguard.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.app.KeyguardManager
*/
public static final String KEYGUARD_SERVICE = "keyguard";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.location.LocationManager} for controlling location
* updates.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.location.LocationManager
*/
public static final String LOCATION_SERVICE = "location";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.location.CountryDetector} for detecting the country that
* the user is in.
*
@@ -3331,96 +3396,100 @@ public abstract class Context {
public static final String COUNTRY_DETECTOR = "country_detector";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.app.SearchManager} for handling searches.
*
+ * <p>
+ * {@link Configuration#UI_MODE_TYPE_WATCH} does not support
+ * {@link android.app.SearchManager}.
+ *
* @see #getSystemService
* @see android.app.SearchManager
*/
public static final String SEARCH_SERVICE = "search";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.hardware.SensorManager} for accessing sensors.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.hardware.SensorManager
*/
public static final String SENSOR_SERVICE = "sensor";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.os.storage.StorageManager} for accessing system storage
* functions.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.os.storage.StorageManager
*/
public static final String STORAGE_SERVICE = "storage";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.app.usage.StorageStatsManager} for accessing system storage
* statistics.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.app.usage.StorageStatsManager
*/
public static final String STORAGE_STATS_SERVICE = "storagestats";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* com.android.server.WallpaperService for accessing wallpapers.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String WALLPAPER_SERVICE = "wallpaper";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.os.Vibrator} for interacting with the vibration hardware.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.os.Vibrator
*/
public static final String VIBRATOR_SERVICE = "vibrator";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.app.StatusBarManager} for interacting with the status bar.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.app.StatusBarManager
* @hide
*/
public static final String STATUS_BAR_SERVICE = "statusbar";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.net.ConnectivityManager} for handling management of
* network connections.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.net.ConnectivityManager
*/
public static final String CONNECTIVITY_SERVICE = "connectivity";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.net.IpSecManager} for encrypting Sockets or Networks with
* IPSec.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String IPSEC_SERVICE = "ipsec";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.os.IUpdateLock} for managing runtime sequences that
* must not be interrupted by headless OTA application or similar.
*
* @hide
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.os.UpdateLock
*/
public static final String UPDATE_LOCK_SERVICE = "updatelock";
@@ -3432,51 +3501,61 @@ public abstract class Context {
public static final String NETWORKMANAGEMENT_SERVICE = "network_management";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a
+ * {@link com.android.server.slice.SliceManagerService} for managing slices.
+ * @hide
+ * @see #getSystemService(String)
+ */
+ public static final String SLICE_SERVICE = "slice";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.app.usage.NetworkStatsManager} for querying network usage stats.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.app.usage.NetworkStatsManager
*/
public static final String NETWORK_STATS_SERVICE = "netstats";
/** {@hide} */
public static final String NETWORK_POLICY_SERVICE = "netpolicy";
+ /** {@hide} */
+ public static final String NETWORK_WATCHLIST_SERVICE = "network_watchlist";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.net.wifi.WifiManager} for handling management of
* Wi-Fi access.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.net.wifi.WifiManager
*/
public static final String WIFI_SERVICE = "wifi";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.net.wifi.p2p.WifiP2pManager} for handling management of
* Wi-Fi peer-to-peer connections.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.net.wifi.p2p.WifiP2pManager
*/
public static final String WIFI_P2P_SERVICE = "wifip2p";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.net.wifi.aware.WifiAwareManager} for handling management of
* Wi-Fi Aware.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.net.wifi.aware.WifiAwareManager
*/
public static final String WIFI_AWARE_SERVICE = "wifiaware";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.net.wifi.WifiScanner} for scanning the wifi universe
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.net.wifi.WifiScanner
* @hide
*/
@@ -3484,22 +3563,35 @@ public abstract class Context {
public static final String WIFI_SCANNING_SERVICE = "wifiscanner";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.net.wifi.RttManager} for ranging devices with wifi
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.net.wifi.RttManager
* @hide
*/
@SystemApi
+ @Deprecated
public static final String WIFI_RTT_SERVICE = "rttmanager";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
+ * android.net.wifi.rtt.WifiRttManager} for ranging devices with wifi
+ *
+ * Note: this is a replacement for WIFI_RTT_SERVICE above. It will
+ * be renamed once final implementation in place.
+ *
+ * @see #getSystemService(String)
+ * @see android.net.wifi.rtt.WifiRttManager
+ */
+ public static final String WIFI_RTT_RANGING_SERVICE = "wifirtt";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.net.lowpan.LowpanManager} for handling management of
* LoWPAN access.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.net.lowpan.LowpanManager
*
* @hide
@@ -3507,11 +3599,11 @@ public abstract class Context {
public static final String LOWPAN_SERVICE = "lowpan";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.net.EthernetManager} for handling management of
* Ethernet access.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.net.EthernetManager
*
* @hide
@@ -3519,98 +3611,98 @@ public abstract class Context {
public static final String ETHERNET_SERVICE = "ethernet";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.net.nsd.NsdManager} for handling management of network service
* discovery
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.net.nsd.NsdManager
*/
public static final String NSD_SERVICE = "servicediscovery";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.media.AudioManager} for handling management of volume,
* ringer modes and audio routing.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.media.AudioManager
*/
public static final String AUDIO_SERVICE = "audio";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.hardware.fingerprint.FingerprintManager} for handling management
* of fingerprints.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.hardware.fingerprint.FingerprintManager
*/
public static final String FINGERPRINT_SERVICE = "fingerprint";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.media.MediaRouter} for controlling and managing
* routing of media.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.media.MediaRouter
*/
public static final String MEDIA_ROUTER_SERVICE = "media_router";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.media.session.MediaSessionManager} for managing media Sessions.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.media.session.MediaSessionManager
*/
public static final String MEDIA_SESSION_SERVICE = "media_session";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.telephony.TelephonyManager} for handling management the
* telephony features of the device.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.telephony.TelephonyManager
*/
public static final String TELEPHONY_SERVICE = "phone";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.telephony.SubscriptionManager} for handling management the
* telephony subscriptions of the device.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.telephony.SubscriptionManager
*/
public static final String TELEPHONY_SUBSCRIPTION_SERVICE = "telephony_subscription_service";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.telecom.TelecomManager} to manage telecom-related features
* of the device.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.telecom.TelecomManager
*/
public static final String TELECOM_SERVICE = "telecom";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.telephony.CarrierConfigManager} for reading carrier configuration values.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.telephony.CarrierConfigManager
*/
public static final String CARRIER_CONFIG_SERVICE = "carrier_config";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.telephony.euicc.EuiccManager} to manage the device eUICC (embedded SIM).
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.telephony.euicc.EuiccManager
*/
public static final String EUICC_SERVICE = "euicc";
@@ -3631,43 +3723,43 @@ public abstract class Context {
* {@link android.content.ClipboardManager} for accessing and modifying
* the contents of the global clipboard.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.content.ClipboardManager
*/
public static final String CLIPBOARD_SERVICE = "clipboard";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link TextClassificationManager} for text classification services.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see TextClassificationManager
*/
public static final String TEXT_CLASSIFICATION_SERVICE = "textclassification";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.view.inputmethod.InputMethodManager} for accessing input
* methods.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String INPUT_METHOD_SERVICE = "input_method";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.view.textservice.TextServicesManager} for accessing
* text services.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String TEXT_SERVICES_MANAGER_SERVICE = "textservices";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.appwidget.AppWidgetManager} for accessing AppWidgets.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String APPWIDGET_SERVICE = "appwidget";
@@ -3675,7 +3767,7 @@ public abstract class Context {
* Official published name of the (internal) voice interaction manager service.
*
* @hide
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String VOICE_INTERACTION_MANAGER_SERVICE = "voiceinteraction";
@@ -3683,119 +3775,119 @@ public abstract class Context {
* Official published name of the (internal) autofill service.
*
* @hide
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String AUTOFILL_MANAGER_SERVICE = "autofill";
/**
- * Use with {@link #getSystemService} to access the
+ * Use with {@link #getSystemService(String)} to access the
* {@link com.android.server.voiceinteraction.SoundTriggerService}.
*
* @hide
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String SOUND_TRIGGER_SERVICE = "soundtrigger";
/**
- * Use with {@link #getSystemService} to retrieve an
+ * Use with {@link #getSystemService(String)} to retrieve an
* {@link android.app.backup.IBackupManager IBackupManager} for communicating
* with the backup mechanism.
* @hide
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
@SystemApi
public static final String BACKUP_SERVICE = "backup";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.os.DropBoxManager} instance for recording
* diagnostic logs.
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String DROPBOX_SERVICE = "dropbox";
/**
- * System service name for the DeviceIdleController. There is no Java API for this.
- * @see #getSystemService
+ * System service name for the DeviceIdleManager.
+ * @see #getSystemService(String)
* @hide
*/
public static final String DEVICE_IDLE_CONTROLLER = "deviceidle";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.app.admin.DevicePolicyManager} for working with global
* device policy management.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String DEVICE_POLICY_SERVICE = "device_policy";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.app.UiModeManager} for controlling UI modes.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String UI_MODE_SERVICE = "uimode";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.app.DownloadManager} for requesting HTTP downloads.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String DOWNLOAD_SERVICE = "download";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.os.BatteryManager} for managing battery state.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String BATTERY_SERVICE = "batterymanager";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.nfc.NfcManager} for using NFC.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String NFC_SERVICE = "nfc";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.bluetooth.BluetoothManager} for using Bluetooth.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String BLUETOOTH_SERVICE = "bluetooth";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.net.sip.SipManager} for accessing the SIP related service.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
/** @hide */
public static final String SIP_SERVICE = "sip";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.hardware.usb.UsbManager} for access to USB devices (as a USB host)
* and for controlling this device's behavior as a USB device.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.hardware.usb.UsbManager
*/
public static final String USB_SERVICE = "usb";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.hardware.SerialManager} for access to serial ports.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.hardware.SerialManager
*
* @hide
@@ -3803,11 +3895,11 @@ public abstract class Context {
public static final String SERIAL_SERVICE = "serial";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.hardware.hdmi.HdmiControlManager} for controlling and managing
* HDMI-CEC protocol.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.hardware.hdmi.HdmiControlManager
* @hide
*/
@@ -3815,67 +3907,67 @@ public abstract class Context {
public static final String HDMI_CONTROL_SERVICE = "hdmi_control";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.hardware.input.InputManager} for interacting with input devices.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.hardware.input.InputManager
*/
public static final String INPUT_SERVICE = "input";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.hardware.display.DisplayManager} for interacting with display devices.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.hardware.display.DisplayManager
*/
public static final String DISPLAY_SERVICE = "display";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.os.UserManager} for managing users on devices that support multiple users.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.os.UserManager
*/
public static final String USER_SERVICE = "user";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.content.pm.LauncherApps} for querying and monitoring launchable apps across
* profiles of a user.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.content.pm.LauncherApps
*/
public static final String LAUNCHER_APPS_SERVICE = "launcherapps";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.content.RestrictionsManager} for retrieving application restrictions
* and requesting permissions for restricted operations.
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.content.RestrictionsManager
*/
public static final String RESTRICTIONS_SERVICE = "restrictions";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.app.AppOpsManager} for tracking application operations
* on the device.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.app.AppOpsManager
*/
public static final String APP_OPS_SERVICE = "appops";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.hardware.camera2.CameraManager} for interacting with
* camera devices.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.hardware.camera2.CameraManager
*/
public static final String CAMERA_SERVICE = "camera";
@@ -3884,51 +3976,51 @@ public abstract class Context {
* {@link android.print.PrintManager} for printing and managing
* printers and print tasks.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.print.PrintManager
*/
public static final String PRINT_SERVICE = "print";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.companion.CompanionDeviceManager} for managing companion devices
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.companion.CompanionDeviceManager
*/
public static final String COMPANION_DEVICE_SERVICE = "companiondevice";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.hardware.ConsumerIrManager} for transmitting infrared
* signals from the device.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.hardware.ConsumerIrManager
*/
public static final String CONSUMER_IR_SERVICE = "consumer_ir";
/**
* {@link android.app.trust.TrustManager} for managing trust agents.
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.app.trust.TrustManager
* @hide
*/
public static final String TRUST_SERVICE = "trust";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.media.tv.TvInputManager} for interacting with TV inputs
* on the device.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.media.tv.TvInputManager
*/
public static final String TV_INPUT_SERVICE = "tv_input";
/**
* {@link android.net.NetworkScoreManager} for managing network scoring.
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.net.NetworkScoreManager
* @hide
*/
@@ -3936,29 +4028,29 @@ public abstract class Context {
public static final String NETWORK_SCORE_SERVICE = "network_score";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.app.usage.UsageStatsManager} for querying device usage stats.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.app.usage.UsageStatsManager
*/
public static final String USAGE_STATS_SERVICE = "usagestats";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.app.job.JobScheduler} instance for managing occasional
* background tasks.
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.app.job.JobScheduler
*/
public static final String JOB_SCHEDULER_SERVICE = "jobscheduler";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.service.persistentdata.PersistentDataBlockManager} instance
* for interacting with a storage device that lives across factory resets.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.service.persistentdata.PersistentDataBlockManager
* @hide
*/
@@ -3966,10 +4058,10 @@ public abstract class Context {
public static final String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.service.oemlock.OemLockManager} instance for managing the OEM lock.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.service.oemlock.OemLockManager
* @hide
*/
@@ -3977,54 +4069,54 @@ public abstract class Context {
public static final String OEM_LOCK_SERVICE = "oem_lock";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.media.projection.MediaProjectionManager} instance for managing
* media projection sessions.
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.media.projection.MediaProjectionManager
*/
public static final String MEDIA_PROJECTION_SERVICE = "media_projection";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.media.midi.MidiManager} for accessing the MIDI service.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String MIDI_SERVICE = "midi";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.hardware.radio.RadioManager} for accessing the broadcast radio service.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @hide
*/
public static final String RADIO_SERVICE = "broadcastradio";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.os.HardwarePropertiesManager} for accessing the hardware properties service.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String HARDWARE_PROPERTIES_SERVICE = "hardware_properties";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.content.pm.ShortcutManager} for accessing the launcher shortcut service.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.content.pm.ShortcutManager
*/
public static final String SHORTCUT_SERVICE = "shortcut";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.hardware.location.ContextHubManager} for accessing context hubs.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.hardware.location.ContextHubManager
*
* @hide
@@ -4033,11 +4125,11 @@ public abstract class Context {
public static final String CONTEXTHUB_SERVICE = "contexthub";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.os.health.SystemHealthManager} for accessing system health (battery, power,
* memory, etc) metrics.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String SYSTEM_HEALTH_SERVICE = "systemhealth";
@@ -4060,35 +4152,56 @@ public abstract class Context {
public static final String INCIDENT_SERVICE = "incident";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Service to assist statsd in obtaining general stats.
+ * @hide
+ */
+ public static final String STATS_COMPANION_SERVICE = "statscompanion";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve an {@link android.app.StatsManager}.
+ * @hide
+ */
+ @SystemApi
+ public static final String STATS_MANAGER = "stats";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.content.om.OverlayManager} for managing overlay packages.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.content.om.OverlayManager
* @hide
*/
public static final String OVERLAY_SERVICE = "overlay";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link VrManager} for accessing the VR service.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @hide
*/
@SystemApi
public static final String VR_SERVICE = "vrmanager";
/**
- * Use with {@link #getSystemService} to retrieve an
+ * Use with {@link #getSystemService(String)} to retrieve an
* {@link android.app.timezone.ITimeZoneRulesManager}.
* @hide
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String TIME_ZONE_RULES_MANAGER_SERVICE = "timezone";
/**
+ * Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.content.pm.CrossProfileApps} for cross profile operations.
+ *
+ * @see #getSystemService(String)
+ */
+ public static final String CROSS_PROFILE_APPS_SERVICE = "crossprofileapps";
+
+ /**
* Use with {@link #getSystemService} to retrieve a
* {@link android.se.omapi.ISecureElementService}
* for accessing the SecureElementService.
@@ -4615,9 +4728,15 @@ public abstract class Context {
*
* @hide
*/
- public abstract Context createPackageContextAsUser(
+ @SystemApi
+ public Context createPackageContextAsUser(
String packageName, @CreatePackageOptions int flags, UserHandle user)
- throws PackageManager.NameNotFoundException;
+ throws PackageManager.NameNotFoundException {
+ if (Build.IS_ENG) {
+ throw new IllegalStateException("createPackageContextAsUser not overridden!");
+ }
+ return this;
+ }
/**
* Creates a context given an {@link android.content.pm.ApplicationInfo}.
@@ -4642,13 +4761,22 @@ public abstract class Context {
throws PackageManager.NameNotFoundException;
/**
- * Get the userId associated with this context
- * @return user id
- *
+ * Get the user associated with this context
* @hide
*/
@TestApi
- public abstract @UserIdInt int getUserId();
+ public UserHandle getUser() {
+ return android.os.Process.myUserHandle();
+ }
+
+ /**
+ * Get the user associated with this context
+ * @hide
+ */
+ @TestApi
+ public @UserIdInt int getUserId() {
+ return android.os.UserHandle.myUserId();
+ }
/**
* Return a new Context object for the current Context but whose resources
@@ -4842,7 +4970,22 @@ public abstract class Context {
/**
* @hide
*/
- public void setAutofillClient(AutofillClient client) {
+ public void setAutofillClient(@SuppressWarnings("unused") AutofillClient client) {
+ }
+
+ /**
+ * @hide
+ */
+ public boolean isAutofillCompatibilityEnabled() {
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ @TestApi
+ public void setAutofillCompatibilityEnabled(
+ @SuppressWarnings("unused") boolean autofillCompatEnabled) {
}
/**
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 3a5fccbbff94..bae99b85d6b8 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -17,6 +17,7 @@
package android.content;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.app.IApplicationThread;
import android.app.IServiceConnection;
import android.content.pm.ApplicationInfo;
@@ -45,6 +46,7 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.util.concurrent.Executor;
/**
* Proxying implementation of Context that simply delegates all of its calls to
@@ -103,7 +105,12 @@ public class ContextWrapper extends Context {
public Looper getMainLooper() {
return mBase.getMainLooper();
}
-
+
+ @Override
+ public Executor getMainExecutor() {
+ return mBase.getMainExecutor();
+ }
+
@Override
public Context getApplicationContext() {
return mBase.getApplicationContext();
@@ -412,8 +419,8 @@ public class ContextWrapper extends Context {
/** @hide */
@Override
- public void startActivitiesAsUser(Intent[] intents, Bundle options, UserHandle userHandle) {
- mBase.startActivitiesAsUser(intents, options, userHandle);
+ public int startActivitiesAsUser(Intent[] intents, Bundle options, UserHandle userHandle) {
+ return mBase.startActivitiesAsUser(intents, options, userHandle);
}
@Override
@@ -995,4 +1002,23 @@ public class ContextWrapper extends Context {
public void setAutofillClient(AutofillClient client) {
mBase.setAutofillClient(client);
}
+
+ /**
+ * @hide
+ */
+ @Override
+ public boolean isAutofillCompatibilityEnabled() {
+ return mBase != null && mBase.isAutofillCompatibilityEnabled();
+ }
+
+ /**
+ * @hide
+ */
+ @TestApi
+ @Override
+ public void setAutofillCompatibilityEnabled(boolean autofillCompatEnabled) {
+ if (mBase != null) {
+ mBase.setAutofillCompatibilityEnabled(autofillCompatEnabled);
+ }
+ }
}
diff --git a/core/java/android/content/CursorLoader.java b/core/java/android/content/CursorLoader.java
index c78871c30a80..5a08636c8fff 100644
--- a/core/java/android/content/CursorLoader.java
+++ b/core/java/android/content/CursorLoader.java
@@ -38,7 +38,11 @@ import java.util.Arrays;
* in the desired paramters with {@link #setUri(Uri)}, {@link #setSelection(String)},
* {@link #setSelectionArgs(String[])}, {@link #setSortOrder(String)},
* and {@link #setProjection(String[])}.
+ *
+ * @deprecated Use the <a href="{@docRoot}tools/extras/support-library.html">Support Library</a>
+ * {@link android.support.v4.content.CursorLoader}
*/
+@Deprecated
public class CursorLoader extends AsyncTaskLoader<Cursor> {
final ForceLoadContentObserver mObserver;
@@ -142,7 +146,7 @@ public class CursorLoader extends AsyncTaskLoader<Cursor> {
}
/**
- * Starts an asynchronous load of the contacts list data. When the result is ready the callbacks
+ * Starts an asynchronous load of the data. When the result is ready the callbacks
* will be called on the UI thread. If a previous load has been completed and is still valid
* the result may be passed to the callbacks immediately.
*
diff --git a/core/java/android/content/IClipboard.aidl b/core/java/android/content/IClipboard.aidl
index af0b8f0593a6..135a4363ef21 100644
--- a/core/java/android/content/IClipboard.aidl
+++ b/core/java/android/content/IClipboard.aidl
@@ -27,6 +27,7 @@ import android.content.IOnPrimaryClipChangedListener;
*/
interface IClipboard {
void setPrimaryClip(in ClipData clip, String callingPackage);
+ void clearPrimaryClip(String callingPackage);
ClipData getPrimaryClip(String pkg);
ClipDescription getPrimaryClipDescription(String callingPackage);
boolean hasPrimaryClip(String callingPackage);
diff --git a/core/java/android/content/IContentService.aidl b/core/java/android/content/IContentService.aidl
index c5001166d77a..dc1766611c3b 100644
--- a/core/java/android/content/IContentService.aidl
+++ b/core/java/android/content/IContentService.aidl
@@ -183,4 +183,6 @@ interface IContentService {
void putCache(in String packageName, in Uri key, in Bundle value, int userId);
Bundle getCache(in String packageName, in Uri key, int userId);
+
+ void resetTodayStats();
}
diff --git a/core/java/android/content/ISyncAdapter.aidl b/core/java/android/content/ISyncAdapter.aidl
index 4660527925c5..0eb581e6b585 100644
--- a/core/java/android/content/ISyncAdapter.aidl
+++ b/core/java/android/content/ISyncAdapter.aidl
@@ -19,6 +19,7 @@ package android.content;
import android.accounts.Account;
import android.os.Bundle;
import android.content.ISyncContext;
+import android.content.ISyncAdapterUnsyncableAccountCallback;
/**
* Interface used to control the sync activity on a SyncAdapter
@@ -26,6 +27,14 @@ import android.content.ISyncContext;
*/
oneway interface ISyncAdapter {
/**
+ * Called before {@link #startSync}. This allows the adapter to defer syncs until the
+ * adapter is ready for the account
+ *
+ * @param cb If called back with {@code false} accounts are not synced.
+ */
+ void onUnsyncableAccount(ISyncAdapterUnsyncableAccountCallback cb);
+
+ /**
* Initiate a sync for this account. SyncAdapter-specific parameters may
* be specified in extras, which is guaranteed to not be null.
*
diff --git a/core/java/android/content/ISyncAdapterUnsyncableAccountCallback.aidl b/core/java/android/content/ISyncAdapterUnsyncableAccountCallback.aidl
new file mode 100644
index 000000000000..a738ac2785c5
--- /dev/null
+++ b/core/java/android/content/ISyncAdapterUnsyncableAccountCallback.aidl
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package android.content;
+
+/**
+ * Callback for {@link ISyncAdapter#onUnsyncableAccount}
+ * @hide
+ */
+oneway interface ISyncAdapterUnsyncableAccountCallback {
+ /**
+ * Deliver the result for {@link ISyncAdapter#onUnsyncableAccount}
+ *
+ * @param isReady Iff {@code false} account is not synced.
+ */
+ void onUnsyncableAccountDone(boolean isReady);
+}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 760fd8031a4d..3dfabdd74a86 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -40,6 +40,7 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import android.os.Process;
import android.os.ResultReceiver;
import android.os.ShellCommand;
@@ -50,9 +51,11 @@ import android.provider.DocumentsContract;
import android.provider.DocumentsProvider;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.util.XmlUtils;
@@ -264,8 +267,8 @@ import java.util.Set;
* </ul>
*
* <p>For example, consider the Note Pad sample application that
- * allows user to browse through a list of notes data and view details about
- * individual items. Text in italics indicate places were you would replace a
+ * allows a user to browse through a list of notes data and view details about
+ * individual items. Text in italics indicates places where you would replace a
* name with one specific to your own package.</p>
*
* <pre> &lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
@@ -1417,6 +1420,9 @@ public class Intent implements Parcelable, Cloneable {
* Activity Action: Start Voice Command.
* <p>Input: Nothing.
* <p>Output: Nothing.
+ * <p class="note">
+ * In some cases, a matching Activity may not exist, so ensure you
+ * safeguard against this.
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_VOICE_COMMAND = "android.intent.action.VOICE_COMMAND";
@@ -1552,16 +1558,6 @@ public class Intent implements Parcelable, Cloneable {
public static final String ACTION_INSTALL_FAILURE = "android.intent.action.INSTALL_FAILURE";
/**
- * @hide
- * @removed
- * @deprecated Do not use. This will go away.
- * Replace with {@link #ACTION_INSTALL_INSTANT_APP_PACKAGE}.
- */
- @SystemApi
- @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
- public static final String ACTION_INSTALL_EPHEMERAL_PACKAGE
- = "android.intent.action.INSTALL_EPHEMERAL_PACKAGE";
- /**
* Activity Action: Launch instant application installer.
* <p class="note">
* This is a protected intent that can only be sent by the system.
@@ -1575,16 +1571,6 @@ public class Intent implements Parcelable, Cloneable {
= "android.intent.action.INSTALL_INSTANT_APP_PACKAGE";
/**
- * @hide
- * @removed
- * @deprecated Do not use. This will go away.
- * Replace with {@link #ACTION_RESOLVE_INSTANT_APP_PACKAGE}.
- */
- @SystemApi
- @SdkConstant(SdkConstantType.SERVICE_ACTION)
- public static final String ACTION_RESOLVE_EPHEMERAL_PACKAGE
- = "android.intent.action.RESOLVE_EPHEMERAL_PACKAGE";
- /**
* Service Action: Resolve instant application.
* <p>
* The system will have a persistent connection to this service.
@@ -1599,16 +1585,6 @@ public class Intent implements Parcelable, Cloneable {
= "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE";
/**
- * @hide
- * @removed
- * @deprecated Do not use. This will go away.
- * Replace with {@link #ACTION_INSTANT_APP_RESOLVER_SETTINGS}.
- */
- @SystemApi
- @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
- public static final String ACTION_EPHEMERAL_RESOLVER_SETTINGS
- = "android.intent.action.EPHEMERAL_RESOLVER_SETTINGS";
- /**
* Activity Action: Launch instant app settings.
*
* <p class="note">
@@ -1727,6 +1703,9 @@ public class Intent implements Parcelable, Cloneable {
* <p>
* Output: If {@link #EXTRA_RETURN_RESULT}, returns whether the install
* succeeded.
+ * <p>
+ * Requires {@link android.Manifest.permission#REQUEST_DELETE_PACKAGES}
+ * since {@link Build.VERSION_CODES#P}.
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_UNINSTALL_PACKAGE = "android.intent.action.UNINSTALL_PACKAGE";
@@ -1839,6 +1818,21 @@ public class Intent implements Parcelable, Cloneable {
public static final String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";
/**
+ * Intent extra: A {@link Bundle} of extras for a package being suspended. Will be sent as an
+ * extra with {@link #ACTION_MY_PACKAGE_SUSPENDED}.
+ *
+ * <p>The contents of this {@link Bundle} are a contract between the suspended app and the
+ * suspending app, i.e. any app with the permission {@code android.permission.SUSPEND_APPS}.
+ * This is meant to enable the suspended app to better handle the state of being suspended.
+ *
+ * @see #ACTION_MY_PACKAGE_SUSPENDED
+ * @see #ACTION_MY_PACKAGE_UNSUSPENDED
+ * @see PackageManager#isPackageSuspended()
+ * @see PackageManager#getSuspendedPackageAppExtras()
+ */
+ public static final String EXTRA_SUSPENDED_PACKAGE_EXTRAS = "android.intent.extra.SUSPENDED_PACKAGE_EXTRAS";
+
+ /**
* Intent extra: An app split name.
* <p>
* Type: String
@@ -1866,6 +1860,17 @@ public class Intent implements Parcelable, Cloneable {
public static final String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED";
/**
+ * Intent extra: A {@link Bundle} of extras supplied for the launcher when any packages on
+ * device are suspended. Will be sent with {@link #ACTION_PACKAGES_SUSPENDED}.
+ *
+ * @see PackageManager#isPackageSuspended()
+ * @see #ACTION_PACKAGES_SUSPENDED
+ *
+ * @hide
+ */
+ public static final String EXTRA_LAUNCHER_EXTRAS = "android.intent.extra.LAUNCHER_EXTRAS";
+
+ /**
* Activity action: Launch UI to manage which apps have a given permission.
* <p>
* Input: {@link #EXTRA_PERMISSION_NAME} specifies the permission access
@@ -2262,6 +2267,70 @@ public class Intent implements Parcelable, Cloneable {
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PACKAGES_UNSUSPENDED = "android.intent.action.PACKAGES_UNSUSPENDED";
+
+ /**
+ * Broadcast Action: Sent to a package that has been suspended by the system. This is sent
+ * whenever a package is put into a suspended state or any of its app extras change while in the
+ * suspended state.
+ * <p> Optionally includes the following extras:
+ * <ul>
+ * <li> {@link #EXTRA_SUSPENDED_PACKAGE_EXTRAS} which is a {@link Bundle} which will contain
+ * useful information for the app being suspended.
+ * </ul>
+ * <p class="note">This is a protected intent that can only be sent
+ * by the system. <em>This will be delivered to {@link BroadcastReceiver} components declared in
+ * the manifest.</em>
+ *
+ * @see #ACTION_MY_PACKAGE_UNSUSPENDED
+ * @see #EXTRA_SUSPENDED_PACKAGE_EXTRAS
+ * @see PackageManager#isPackageSuspended()
+ * @see PackageManager#getSuspendedPackageAppExtras()
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_MY_PACKAGE_SUSPENDED = "android.intent.action.MY_PACKAGE_SUSPENDED";
+
+ /**
+ * Activity Action: Started to show more details about why an application was suspended.
+ *
+ * <p>Whenever the system detects an activity launch for a suspended app, this action can
+ * be used to show more details about the reason for suspension.
+ *
+ * <p>Apps holding {@link android.Manifest.permission#SUSPEND_APPS} must declare an activity
+ * handling this intent and protect it with
+ * {@link android.Manifest.permission#SEND_SHOW_SUSPENDED_APP_DETAILS}.
+ *
+ * <p>Includes an extra {@link #EXTRA_PACKAGE_NAME} which is the name of the suspended package.
+ *
+ * <p class="note">This is a protected intent that can only be sent
+ * by the system.
+ *
+ * @see PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
+ * PersistableBundle, String)
+ * @see PackageManager#isPackageSuspended()
+ * @see #ACTION_PACKAGES_SUSPENDED
+ *
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_SHOW_SUSPENDED_APP_DETAILS =
+ "android.intent.action.SHOW_SUSPENDED_APP_DETAILS";
+
+ /**
+ * Broadcast Action: Sent to a package that has been unsuspended.
+ *
+ * <p class="note">This is a protected intent that can only be sent
+ * by the system. <em>This will be delivered to {@link BroadcastReceiver} components declared in
+ * the manifest.</em>
+ *
+ * @see #ACTION_MY_PACKAGE_SUSPENDED
+ * @see #EXTRA_SUSPENDED_PACKAGE_EXTRAS
+ * @see PackageManager#isPackageSuspended()
+ * @see PackageManager#getSuspendedPackageAppExtras()
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_MY_PACKAGE_UNSUSPENDED = "android.intent.action.MY_PACKAGE_UNSUSPENDED";
+
/**
* Broadcast Action: A user ID has been removed from the system. The user
* ID number is stored in the extra data under {@link #EXTRA_UID}.
@@ -2424,6 +2493,26 @@ public class Intent implements Parcelable, Cloneable {
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONFIGURATION_CHANGED = "android.intent.action.CONFIGURATION_CHANGED";
+
+ /**
+ * Broadcast Action: The current device {@link android.content.res.Configuration} has changed
+ * such that the device may be eligible for the installation of additional configuration splits.
+ * Configuration properties that can trigger this broadcast include locale and display density.
+ *
+ * <p class="note">
+ * Unlike {@link #ACTION_CONFIGURATION_CHANGED}, you <em>can</em> receive this through
+ * components declared in manifests. However, the receiver <em>must</em> hold the
+ * {@link android.Manifest.permission#INSTALL_PACKAGES} permission.
+ *
+ * <p class="note">
+ * This is a protected intent that can only be sent by the system.
+ *
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_SPLIT_CONFIGURATION_CHANGED =
+ "android.intent.action.SPLIT_CONFIGURATION_CHANGED";
/**
* Broadcast Action: The current device's locale has changed.
*
@@ -2453,6 +2542,23 @@ public class Intent implements Parcelable, Cloneable {
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_BATTERY_CHANGED = "android.intent.action.BATTERY_CHANGED";
+
+
+ /**
+ * Broadcast Action: Sent when the current battery level changes.
+ *
+ * It has {@link android.os.BatteryManager#EXTRA_EVENTS} that carries a list of {@link Bundle}
+ * instances representing individual battery level changes with associated
+ * extras from {@link #ACTION_BATTERY_CHANGED}.
+ *
+ * <p class="note">
+ * This broadcast requires {@link android.Manifest.permission#BATTERY_STATS} permission.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String ACTION_BATTERY_LEVEL_CHANGED =
+ "android.intent.action.BATTERY_LEVEL_CHANGED";
/**
* Broadcast Action: Indicates low battery condition on the device.
* This broadcast corresponds to the "Low battery warning" system dialog.
@@ -2503,6 +2609,9 @@ public class Intent implements Parcelable, Cloneable {
* off, not sleeping). Once the broadcast is complete, the final shutdown
* will proceed and all unsaved data lost. Apps will not normally need
* to handle this, since the foreground activity will be paused as well.
+ * <p>As of {@link Build.VERSION_CODES#P} this broadcast is only sent to receivers registered
+ * through {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
+ * Context.registerReceiver}.
*
* <p class="note">This is a protected intent that can only be sent
* by the system.
@@ -3443,11 +3552,12 @@ public class Intent implements Parcelable, Cloneable {
/**
* A broadcast action to trigger a factory reset.
*
- * <p> The sender must hold the {@link android.Manifest.permission#MASTER_CLEAR} permission.
+ * <p>The sender must hold the {@link android.Manifest.permission#MASTER_CLEAR} permission. The
+ * reason for the factory reset should be specified as {@link #EXTRA_REASON}.
*
* <p>Not for use by third-party applications.
*
- * @see #EXTRA_FORCE_MASTER_CLEAR
+ * @see #EXTRA_FORCE_FACTORY_RESET
*
* {@hide}
*/
@@ -3876,6 +3986,26 @@ public class Intent implements Parcelable, Cloneable {
*/
public static final int EXTRA_THERMAL_STATE_EXCEEDED = 2;
+ /**
+ * Broadcast Action: Indicates the dock in idle state while device is docked.
+ *
+ * <p class="note">This is a protected intent that can only be sent
+ * by the system.
+ *
+ * @hide
+ */
+ public static final String ACTION_DOCK_IDLE = "android.intent.action.DOCK_IDLE";
+
+ /**
+ * Broadcast Action: Indicates the dock in active state while device is docked.
+ *
+ * <p class="note">This is a protected intent that can only be sent
+ * by the system.
+ *
+ * @hide
+ */
+ public static final String ACTION_DOCK_ACTIVE = "android.intent.action.DOCK_ACTIVE";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
@@ -3960,6 +4090,14 @@ public class Intent implements Parcelable, Cloneable {
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_LEANBACK_LAUNCHER = "android.intent.category.LEANBACK_LAUNCHER";
/**
+ * Indicates the preferred entry-point activity when an application is launched from a Car
+ * launcher. If not present, Car launcher can optionally use {@link #CATEGORY_LAUNCHER} as a
+ * fallback, or exclude the application entirely.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+ public static final String CATEGORY_CAR_LAUNCHER = "android.intent.category.CAR_LAUNCHER";
+ /**
* Indicates a Leanback settings activity to be displayed in the Leanback launcher.
* @hide
*/
@@ -4465,38 +4603,128 @@ public class Intent implements Parcelable, Cloneable {
/**
* A {@link IntentSender} to start after ephemeral installation success.
+ * @deprecated Use {@link #EXTRA_INSTANT_APP_SUCCESS).
+ * @removed
* @hide
*/
+ @Deprecated
public static final String EXTRA_EPHEMERAL_SUCCESS = "android.intent.extra.EPHEMERAL_SUCCESS";
/**
+ * A {@link IntentSender} to start after instant app installation success.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_INSTANT_APP_SUCCESS =
+ "android.intent.extra.INSTANT_APP_SUCCESS";
+
+ /**
* A {@link IntentSender} to start after ephemeral installation failure.
+ * @deprecated Use {@link #EXTRA_INSTANT_APP_FAILURE).
+ * @removed
* @hide
*/
+ @Deprecated
public static final String EXTRA_EPHEMERAL_FAILURE = "android.intent.extra.EPHEMERAL_FAILURE";
/**
+ * A {@link IntentSender} to start after instant app installation failure.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_INSTANT_APP_FAILURE =
+ "android.intent.extra.INSTANT_APP_FAILURE";
+
+ /**
* The host name that triggered an ephemeral resolution.
+ * @deprecated Use {@link #EXTRA_INSTANT_APP_HOSTNAME).
+ * @removed
* @hide
*/
+ @Deprecated
public static final String EXTRA_EPHEMERAL_HOSTNAME = "android.intent.extra.EPHEMERAL_HOSTNAME";
/**
+ * The host name that triggered an instant app resolution.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_INSTANT_APP_HOSTNAME =
+ "android.intent.extra.INSTANT_APP_HOSTNAME";
+
+ /**
* An opaque token to track ephemeral resolution.
+ * @deprecated Use {@link #EXTRA_INSTANT_APP_TOKEN).
+ * @removed
* @hide
*/
+ @Deprecated
public static final String EXTRA_EPHEMERAL_TOKEN = "android.intent.extra.EPHEMERAL_TOKEN";
/**
+ * An opaque token to track instant app resolution.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_INSTANT_APP_TOKEN =
+ "android.intent.extra.INSTANT_APP_TOKEN";
+
+ /**
+ * The action that triggered an instant application resolution.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_INSTANT_APP_ACTION = "android.intent.extra.INSTANT_APP_ACTION";
+
+ /**
+ * An array of {@link Bundle}s containing details about resolved instant apps..
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_INSTANT_APP_BUNDLES =
+ "android.intent.extra.INSTANT_APP_BUNDLES";
+
+ /**
+ * A {@link Bundle} of metadata that describes the instant application that needs to be
+ * installed. This data is populated from the response to
+ * {@link android.content.pm.InstantAppResolveInfo#getExtras()} as provided by the registered
+ * instant application resolver.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_INSTANT_APP_EXTRAS =
+ "android.intent.extra.INSTANT_APP_EXTRAS";
+
+ /**
+ * A boolean value indicating that the instant app resolver was unable to state with certainty
+ * that it did or did not have an app for the sanitized {@link Intent} defined at
+ * {@link #EXTRA_INTENT}.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_UNKNOWN_INSTANT_APP =
+ "android.intent.extra.UNKNOWN_INSTANT_APP";
+
+ /**
* The version code of the app to install components from.
+ * @deprecated Use {@link #EXTRA_LONG_VERSION_CODE).
* @hide
*/
+ @Deprecated
public static final String EXTRA_VERSION_CODE = "android.intent.extra.VERSION_CODE";
/**
- * The app that triggered the ephemeral installation.
+ * The version code of the app to install components from.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_LONG_VERSION_CODE = "android.intent.extra.LONG_VERSION_CODE";
+
+ /**
+ * The app that triggered the instant app installation.
* @hide
*/
+ @SystemApi
public static final String EXTRA_CALLING_PACKAGE
= "android.intent.extra.CALLING_PACKAGE";
@@ -4505,6 +4733,7 @@ public class Intent implements Parcelable, Cloneable {
* installer may use.
* @hide
*/
+ @SystemApi
public static final String EXTRA_VERIFICATION_BUNDLE
= "android.intent.extra.VERIFICATION_BUNDLE";
@@ -4864,7 +5093,13 @@ public class Intent implements Parcelable, Cloneable {
/** @hide */
public static final int EXTRA_TIME_PREF_VALUE_USE_LOCALE_DEFAULT = 2;
- /** {@hide} */
+ /**
+ * Intent extra: the reason that the operation associated with this intent is being performed.
+ *
+ * <p>Type: String
+ * @hide
+ */
+ @SystemApi
public static final String EXTRA_REASON = "android.intent.extra.REASON";
/**
@@ -4918,8 +5153,9 @@ public class Intent implements Parcelable, Cloneable {
* <li>Enumeration of features here is not meant to restrict capabilities of the quick viewer.
* Quick viewer can implement features not listed below.
* <li>Features included at this time are: {@link QuickViewConstants#FEATURE_VIEW},
- * {@link QuickViewConstants#FEATURE_EDIT}, {@link QuickViewConstants#FEATURE_DOWNLOAD},
- * {@link QuickViewConstants#FEATURE_SEND}, {@link QuickViewConstants#FEATURE_PRINT}.
+ * {@link QuickViewConstants#FEATURE_EDIT}, {@link QuickViewConstants#FEATURE_DELETE},
+ * {@link QuickViewConstants#FEATURE_DOWNLOAD}, {@link QuickViewConstants#FEATURE_SEND},
+ * {@link QuickViewConstants#FEATURE_PRINT}.
* <p>
* Requirements:
* <li>Quick viewer shouldn't show a feature if the feature is absent in
@@ -5020,6 +5256,7 @@ public class Intent implements Parcelable, Cloneable {
FLAG_GRANT_PREFIX_URI_PERMISSION,
FLAG_DEBUG_TRIAGED_MISSING,
FLAG_IGNORE_EPHEMERAL,
+ FLAG_ACTIVITY_MATCH_EXTERNAL,
FLAG_ACTIVITY_NO_HISTORY,
FLAG_ACTIVITY_SINGLE_TOP,
FLAG_ACTIVITY_NEW_TASK,
@@ -5063,6 +5300,7 @@ public class Intent implements Parcelable, Cloneable {
FLAG_INCLUDE_STOPPED_PACKAGES,
FLAG_DEBUG_TRIAGED_MISSING,
FLAG_IGNORE_EPHEMERAL,
+ FLAG_ACTIVITY_MATCH_EXTERNAL,
FLAG_ACTIVITY_NO_HISTORY,
FLAG_ACTIVITY_SINGLE_TOP,
FLAG_ACTIVITY_NEW_TASK,
@@ -5466,6 +5704,29 @@ public class Intent implements Parcelable, Cloneable {
*/
public static final int FLAG_ACTIVITY_LAUNCH_ADJACENT = 0x00001000;
+
+ /**
+ * If set in an Intent passed to {@link Context#startActivity Context.startActivity()},
+ * this flag will attempt to launch an instant app if no full app on the device can already
+ * handle the intent.
+ * <p>
+ * When attempting to resolve instant apps externally, the following {@link Intent} properties
+ * are supported:
+ * <ul>
+ * <li>{@link Intent#setAction(String)}</li>
+ * <li>{@link Intent#addCategory(String)}</li>
+ * <li>{@link Intent#setData(Uri)}</li>
+ * <li>{@link Intent#setType(String)}</li>
+ * <li>{@link Intent#setPackage(String)}</li>
+ * <li>{@link Intent#addFlags(int)}</li>
+ * </ul>
+ * <p>
+ * In the case that no instant app can be found, the installer will be launched to notify the
+ * user that the intent could not be resolved. On devices that do not support instant apps,
+ * the flag will be ignored.
+ */
+ public static final int FLAG_ACTIVITY_MATCH_EXTERNAL = 0x00000800;
+
/**
* If set, when sending a broadcast only registered receivers will be
* called -- no BroadcastReceiver components will be launched.
@@ -5692,11 +5953,14 @@ public class Intent implements Parcelable, Cloneable {
private static final int COPY_MODE_HISTORY = 2;
/** @hide */
- @IntDef(value = {COPY_MODE_ALL, COPY_MODE_FILTER, COPY_MODE_HISTORY})
+ @IntDef(prefix = { "COPY_MODE_" }, value = {
+ COPY_MODE_ALL,
+ COPY_MODE_FILTER,
+ COPY_MODE_HISTORY
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface CopyMode {}
-
/**
* Create an empty intent.
*/
@@ -6629,6 +6893,9 @@ public class Intent implements Parcelable, Cloneable {
case "--activity-task-on-home":
intent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME);
break;
+ case "--activity-match-external":
+ intent.addFlags(Intent.FLAG_ACTIVITY_MATCH_EXTERNAL);
+ break;
case "--receiver-registered-only":
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
break;
@@ -6727,6 +6994,7 @@ public class Intent implements Parcelable, Cloneable {
"<INTENT> specifications include these flags and arguments:",
" [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]",
" [-c <CATEGORY> [-c <CATEGORY>] ...]",
+ " [-n <COMPONENT_NAME>]",
" [-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...]",
" [--esn <EXTRA_KEY> ...]",
" [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]",
@@ -6765,7 +7033,7 @@ public class Intent implements Parcelable, Cloneable {
" [--activity-no-user-action] [--activity-previous-is-top]",
" [--activity-reorder-to-front] [--activity-reset-task-if-needed]",
" [--activity-single-top] [--activity-clear-task]",
- " [--activity-task-on-home]",
+ " [--activity-task-on-home] [--activity-match-external]",
" [--receiver-registered-only] [--receiver-replace-pending]",
" [--receiver-foreground] [--receiver-no-abort]",
" [--receiver-include-background]",
@@ -7016,7 +7284,7 @@ public class Intent implements Parcelable, Cloneable {
*
* @param name The name of the desired item.
*
- * @return the value of an item that previously added with putExtra()
+ * @return the value of an item previously added with putExtra(),
* or null if none was found.
*
* @deprecated
@@ -7034,7 +7302,7 @@ public class Intent implements Parcelable, Cloneable {
* @param defaultValue the value to be returned if no value of the desired
* type is stored with the given name.
*
- * @return the value of an item that previously added with putExtra()
+ * @return the value of an item previously added with putExtra(),
* or the default value if none was found.
*
* @see #putExtra(String, boolean)
@@ -7051,7 +7319,7 @@ public class Intent implements Parcelable, Cloneable {
* @param defaultValue the value to be returned if no value of the desired
* type is stored with the given name.
*
- * @return the value of an item that previously added with putExtra()
+ * @return the value of an item previously added with putExtra(),
* or the default value if none was found.
*
* @see #putExtra(String, byte)
@@ -7068,7 +7336,7 @@ public class Intent implements Parcelable, Cloneable {
* @param defaultValue the value to be returned if no value of the desired
* type is stored with the given name.
*
- * @return the value of an item that previously added with putExtra()
+ * @return the value of an item previously added with putExtra(),
* or the default value if none was found.
*
* @see #putExtra(String, short)
@@ -7085,7 +7353,7 @@ public class Intent implements Parcelable, Cloneable {
* @param defaultValue the value to be returned if no value of the desired
* type is stored with the given name.
*
- * @return the value of an item that previously added with putExtra()
+ * @return the value of an item previously added with putExtra(),
* or the default value if none was found.
*
* @see #putExtra(String, char)
@@ -7102,7 +7370,7 @@ public class Intent implements Parcelable, Cloneable {
* @param defaultValue the value to be returned if no value of the desired
* type is stored with the given name.
*
- * @return the value of an item that previously added with putExtra()
+ * @return the value of an item previously added with putExtra(),
* or the default value if none was found.
*
* @see #putExtra(String, int)
@@ -7119,7 +7387,7 @@ public class Intent implements Parcelable, Cloneable {
* @param defaultValue the value to be returned if no value of the desired
* type is stored with the given name.
*
- * @return the value of an item that previously added with putExtra()
+ * @return the value of an item previously added with putExtra(),
* or the default value if none was found.
*
* @see #putExtra(String, long)
@@ -7136,7 +7404,7 @@ public class Intent implements Parcelable, Cloneable {
* @param defaultValue the value to be returned if no value of the desired
* type is stored with the given name.
*
- * @return the value of an item that previously added with putExtra(),
+ * @return the value of an item previously added with putExtra(),
* or the default value if no such item is present
*
* @see #putExtra(String, float)
@@ -7153,7 +7421,7 @@ public class Intent implements Parcelable, Cloneable {
* @param defaultValue the value to be returned if no value of the desired
* type is stored with the given name.
*
- * @return the value of an item that previously added with putExtra()
+ * @return the value of an item previously added with putExtra(),
* or the default value if none was found.
*
* @see #putExtra(String, double)
@@ -7168,7 +7436,7 @@ public class Intent implements Parcelable, Cloneable {
*
* @param name The name of the desired item.
*
- * @return the value of an item that previously added with putExtra()
+ * @return the value of an item previously added with putExtra(),
* or null if no String value was found.
*
* @see #putExtra(String, String)
@@ -7182,7 +7450,7 @@ public class Intent implements Parcelable, Cloneable {
*
* @param name The name of the desired item.
*
- * @return the value of an item that previously added with putExtra()
+ * @return the value of an item previously added with putExtra(),
* or null if no CharSequence value was found.
*
* @see #putExtra(String, CharSequence)
@@ -7196,7 +7464,7 @@ public class Intent implements Parcelable, Cloneable {
*
* @param name The name of the desired item.
*
- * @return the value of an item that previously added with putExtra()
+ * @return the value of an item previously added with putExtra(),
* or null if no Parcelable value was found.
*
* @see #putExtra(String, Parcelable)
@@ -7210,7 +7478,7 @@ public class Intent implements Parcelable, Cloneable {
*
* @param name The name of the desired item.
*
- * @return the value of an item that previously added with putExtra()
+ * @return the value of an item previously added with putExtra(),
* or null if no Parcelable[] value was found.
*
* @see #putExtra(String, Parcelable[])
@@ -7224,8 +7492,9 @@ public class Intent implements Parcelable, Cloneable {
*
* @param name The name of the desired item.
*
- * @return the value of an item that previously added with putExtra()
- * or null if no ArrayList<Parcelable> value was found.
+ * @return the value of an item previously added with
+ * putParcelableArrayListExtra(), or null if no
+ * ArrayList<Parcelable> value was found.
*
* @see #putParcelableArrayListExtra(String, ArrayList)
*/
@@ -7238,7 +7507,7 @@ public class Intent implements Parcelable, Cloneable {
*
* @param name The name of the desired item.
*
- * @return the value of an item that previously added with putExtra()
+ * @return the value of an item previously added with putExtra(),
* or null if no Serializable value was found.
*
* @see #putExtra(String, Serializable)
@@ -7252,8 +7521,9 @@ public class Intent implements Parcelable, Cloneable {
*
* @param name The name of the desired item.
*
- * @return the value of an item that previously added with putExtra()
- * or null if no ArrayList<Integer> value was found.
+ * @return the value of an item previously added with
+ * putIntegerArrayListExtra(), or null if no
+ * ArrayList<Integer> value was found.
*
* @see #putIntegerArrayListExtra(String, ArrayList)
*/
@@ -7266,8 +7536,9 @@ public class Intent implements Parcelable, Cloneable {
*
* @param name The name of the desired item.
*
- * @return the value of an item that previously added with putExtra()
- * or null if no ArrayList<String> value was found.
+ * @return the value of an item previously added with
+ * putStringArrayListExtra(), or null if no
+ * ArrayList<String> value was found.
*
* @see #putStringArrayListExtra(String, ArrayList)
*/
@@ -7280,8 +7551,9 @@ public class Intent implements Parcelable, Cloneable {
*
* @param name The name of the desired item.
*
- * @return the value of an item that previously added with putExtra()
- * or null if no ArrayList<CharSequence> value was found.
+ * @return the value of an item previously added with
+ * putCharSequenceArrayListExtra, or null if no
+ * ArrayList<CharSequence> value was found.
*
* @see #putCharSequenceArrayListExtra(String, ArrayList)
*/
@@ -7294,7 +7566,7 @@ public class Intent implements Parcelable, Cloneable {
*
* @param name The name of the desired item.
*
- * @return the value of an item that previously added with putExtra()
+ * @return the value of an item previously added with putExtra(),
* or null if no boolean array value was found.
*
* @see #putExtra(String, boolean[])
@@ -7308,7 +7580,7 @@ public class Intent implements Parcelable, Cloneable {
*
* @param name The name of the desired item.
*
- * @return the value of an item that previously added with putExtra()
+ * @return the value of an item previously added with putExtra(),
* or null if no byte array value was found.
*
* @see #putExtra(String, byte[])
@@ -7322,7 +7594,7 @@ public class Intent implements Parcelable, Cloneable {
*
* @param name The name of the desired item.
*
- * @return the value of an item that previously added with putExtra()
+ * @return the value of an item previously added with putExtra(),
* or null if no short array value was found.
*
* @see #putExtra(String, short[])
@@ -7336,7 +7608,7 @@ public class Intent implements Parcelable, Cloneable {
*
* @param name The name of the desired item.
*
- * @return the value of an item that previously added with putExtra()
+ * @return the value of an item previously added with putExtra(),
* or null if no char array value was found.
*
* @see #putExtra(String, char[])
@@ -7350,7 +7622,7 @@ public class Intent implements Parcelable, Cloneable {
*
* @param name The name of the desired item.
*
- * @return the value of an item that previously added with putExtra()
+ * @return the value of an item previously added with putExtra(),
* or null if no int array value was found.
*
* @see #putExtra(String, int[])
@@ -7364,7 +7636,7 @@ public class Intent implements Parcelable, Cloneable {
*
* @param name The name of the desired item.
*
- * @return the value of an item that previously added with putExtra()
+ * @return the value of an item previously added with putExtra(),
* or null if no long array value was found.
*
* @see #putExtra(String, long[])
@@ -7378,7 +7650,7 @@ public class Intent implements Parcelable, Cloneable {
*
* @param name The name of the desired item.
*
- * @return the value of an item that previously added with putExtra()
+ * @return the value of an item previously added with putExtra(),
* or null if no float array value was found.
*
* @see #putExtra(String, float[])
@@ -7392,7 +7664,7 @@ public class Intent implements Parcelable, Cloneable {
*
* @param name The name of the desired item.
*
- * @return the value of an item that previously added with putExtra()
+ * @return the value of an item previously added with putExtra(),
* or null if no double array value was found.
*
* @see #putExtra(String, double[])
@@ -7406,7 +7678,7 @@ public class Intent implements Parcelable, Cloneable {
*
* @param name The name of the desired item.
*
- * @return the value of an item that previously added with putExtra()
+ * @return the value of an item previously added with putExtra(),
* or null if no String array value was found.
*
* @see #putExtra(String, String[])
@@ -7420,7 +7692,7 @@ public class Intent implements Parcelable, Cloneable {
*
* @param name The name of the desired item.
*
- * @return the value of an item that previously added with putExtra()
+ * @return the value of an item previously added with putExtra(),
* or null if no CharSequence array value was found.
*
* @see #putExtra(String, CharSequence[])
@@ -7434,7 +7706,7 @@ public class Intent implements Parcelable, Cloneable {
*
* @param name The name of the desired item.
*
- * @return the value of an item that previously added with putExtra()
+ * @return the value of an item previously added with putExtra(),
* or null if no Bundle value was found.
*
* @see #putExtra(String, Bundle)
@@ -7448,7 +7720,7 @@ public class Intent implements Parcelable, Cloneable {
*
* @param name The name of the desired item.
*
- * @return the value of an item that previously added with putExtra()
+ * @return the value of an item previously added with putExtra(),
* or null if no IBinder value was found.
*
* @see #putExtra(String, IBinder)
@@ -7468,7 +7740,7 @@ public class Intent implements Parcelable, Cloneable {
* @param defaultValue The default value to return in case no item is
* associated with the key 'name'
*
- * @return the value of an item that previously added with putExtra()
+ * @return the value of an item previously added with putExtra(),
* or defaultValue if none was found.
*
* @see #putExtra
@@ -8968,17 +9240,16 @@ public class Intent implements Parcelable, Cloneable {
}
/** @hide */
- @IntDef(flag = true,
- value = {
- FILL_IN_ACTION,
- FILL_IN_DATA,
- FILL_IN_CATEGORIES,
- FILL_IN_COMPONENT,
- FILL_IN_PACKAGE,
- FILL_IN_SOURCE_BOUNDS,
- FILL_IN_SELECTOR,
- FILL_IN_CLIP_DATA
- })
+ @IntDef(flag = true, prefix = { "FILL_IN_" }, value = {
+ FILL_IN_ACTION,
+ FILL_IN_DATA,
+ FILL_IN_CATEGORIES,
+ FILL_IN_COMPONENT,
+ FILL_IN_PACKAGE,
+ FILL_IN_SOURCE_BOUNDS,
+ FILL_IN_SELECTOR,
+ FILL_IN_CLIP_DATA
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface FillInFlags {}
@@ -9409,6 +9680,63 @@ public class Intent implements Parcelable, Cloneable {
}
}
+ /** @hide */
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ // Same input parameters that toString() gives to toShortString().
+ writeToProto(proto, fieldId, true, true, true, false);
+ }
+
+ /** @hide */
+ public void writeToProto(ProtoOutputStream proto, long fieldId, boolean secure, boolean comp,
+ boolean extras, boolean clip) {
+ long token = proto.start(fieldId);
+ if (mAction != null) {
+ proto.write(IntentProto.ACTION, mAction);
+ }
+ if (mCategories != null) {
+ for (String category : mCategories) {
+ proto.write(IntentProto.CATEGORIES, category);
+ }
+ }
+ if (mData != null) {
+ proto.write(IntentProto.DATA, secure ? mData.toSafeString() : mData.toString());
+ }
+ if (mType != null) {
+ proto.write(IntentProto.TYPE, mType);
+ }
+ if (mFlags != 0) {
+ proto.write(IntentProto.FLAG, "0x" + Integer.toHexString(mFlags));
+ }
+ if (mPackage != null) {
+ proto.write(IntentProto.PACKAGE, mPackage);
+ }
+ if (comp && mComponent != null) {
+ mComponent.writeToProto(proto, IntentProto.COMPONENT);
+ }
+ if (mSourceBounds != null) {
+ proto.write(IntentProto.SOURCE_BOUNDS, mSourceBounds.toShortString());
+ }
+ if (mClipData != null) {
+ StringBuilder b = new StringBuilder();
+ if (clip) {
+ mClipData.toShortString(b);
+ } else {
+ mClipData.toShortStringShortItems(b, false);
+ }
+ proto.write(IntentProto.CLIP_DATA, b.toString());
+ }
+ if (extras && mExtras != null) {
+ proto.write(IntentProto.EXTRAS, mExtras.toShortString());
+ }
+ if (mContentUserHint != 0) {
+ proto.write(IntentProto.CONTENT_USER_HINT, mContentUserHint);
+ }
+ if (mSelector != null) {
+ proto.write(IntentProto.SELECTOR, mSelector.toShortString(secure, comp, extras, clip));
+ }
+ proto.end(token);
+ }
+
/**
* Call {@link #toUri} with 0 flags.
* @deprecated Use {@link #toUri} instead.
@@ -9956,6 +10284,24 @@ public class Intent implements Parcelable, Cloneable {
}
}
+ /** @hide */
+ public boolean hasWebURI() {
+ if (getData() == null) {
+ return false;
+ }
+ final String scheme = getScheme();
+ if (TextUtils.isEmpty(scheme)) {
+ return false;
+ }
+ return scheme.equals(IntentFilter.SCHEME_HTTP) || scheme.equals(IntentFilter.SCHEME_HTTPS);
+ }
+
+ /** @hide */
+ public boolean isWebIntent() {
+ return ACTION_VIEW.equals(mAction)
+ && hasWebURI();
+ }
+
/**
* @hide
*/
@@ -10109,6 +10455,27 @@ public class Intent implements Parcelable, Cloneable {
return false;
}
+ /**
+ * Convert the dock state to a human readable format.
+ * @hide
+ */
+ public static String dockStateToString(int dock) {
+ switch (dock) {
+ case EXTRA_DOCK_STATE_HE_DESK:
+ return "EXTRA_DOCK_STATE_HE_DESK";
+ case EXTRA_DOCK_STATE_LE_DESK:
+ return "EXTRA_DOCK_STATE_LE_DESK";
+ case EXTRA_DOCK_STATE_CAR:
+ return "EXTRA_DOCK_STATE_CAR";
+ case EXTRA_DOCK_STATE_DESK:
+ return "EXTRA_DOCK_STATE_DESK";
+ case EXTRA_DOCK_STATE_UNDOCKED:
+ return "EXTRA_DOCK_STATE_UNDOCKED";
+ default:
+ return Integer.toString(dock);
+ }
+ }
+
private static ClipData.Item makeClipItem(ArrayList<Uri> streams, ArrayList<CharSequence> texts,
ArrayList<String> htmlTexts, int which) {
Uri uri = streams != null ? streams.get(which) : null;
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index c9bce530be3e..cec3badd2e6c 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -26,6 +26,7 @@ import android.text.TextUtils;
import android.util.AndroidException;
import android.util.Log;
import android.util.Printer;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.util.XmlUtils;
@@ -918,6 +919,15 @@ public class IntentFilter implements Parcelable {
dest.writeInt(mPort);
}
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ long token = proto.start(fieldId);
+ // The original host information is already contained in host and wild, no output now.
+ proto.write(AuthorityEntryProto.HOST, mHost);
+ proto.write(AuthorityEntryProto.WILD, mWild);
+ proto.write(AuthorityEntryProto.PORT, mPort);
+ proto.end(token);
+ }
+
public String getHost() {
return mOrigHost;
}
@@ -1739,6 +1749,59 @@ public class IntentFilter implements Parcelable {
}
}
+ /** @hide */
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ long token = proto.start(fieldId);
+ if (mActions.size() > 0) {
+ Iterator<String> it = mActions.iterator();
+ while (it.hasNext()) {
+ proto.write(IntentFilterProto.ACTIONS, it.next());
+ }
+ }
+ if (mCategories != null) {
+ Iterator<String> it = mCategories.iterator();
+ while (it.hasNext()) {
+ proto.write(IntentFilterProto.CATEGORIES, it.next());
+ }
+ }
+ if (mDataSchemes != null) {
+ Iterator<String> it = mDataSchemes.iterator();
+ while (it.hasNext()) {
+ proto.write(IntentFilterProto.DATA_SCHEMES, it.next());
+ }
+ }
+ if (mDataSchemeSpecificParts != null) {
+ Iterator<PatternMatcher> it = mDataSchemeSpecificParts.iterator();
+ while (it.hasNext()) {
+ it.next().writeToProto(proto, IntentFilterProto.DATA_SCHEME_SPECS);
+ }
+ }
+ if (mDataAuthorities != null) {
+ Iterator<AuthorityEntry> it = mDataAuthorities.iterator();
+ while (it.hasNext()) {
+ it.next().writeToProto(proto, IntentFilterProto.DATA_AUTHORITIES);
+ }
+ }
+ if (mDataPaths != null) {
+ Iterator<PatternMatcher> it = mDataPaths.iterator();
+ while (it.hasNext()) {
+ it.next().writeToProto(proto, IntentFilterProto.DATA_PATHS);
+ }
+ }
+ if (mDataTypes != null) {
+ Iterator<String> it = mDataTypes.iterator();
+ while (it.hasNext()) {
+ proto.write(IntentFilterProto.DATA_TYPES, it.next());
+ }
+ }
+ if (mPriority != 0 || mHasPartialTypes) {
+ proto.write(IntentFilterProto.PRIORITY, mPriority);
+ proto.write(IntentFilterProto.HAS_PARTIAL_TYPES, mHasPartialTypes);
+ }
+ proto.write(IntentFilterProto.GET_AUTO_VERIFY, getAutoVerify());
+ proto.end(token);
+ }
+
public void dump(Printer du, String prefix) {
StringBuilder sb = new StringBuilder(256);
if (mActions.size() > 0) {
@@ -1809,9 +1872,10 @@ public class IntentFilter implements Parcelable {
du.println(sb.toString());
}
}
- if (mPriority != 0 || mHasPartialTypes) {
+ if (mPriority != 0 || mOrder != 0 || mHasPartialTypes) {
sb.setLength(0);
sb.append(prefix); sb.append("mPriority="); sb.append(mPriority);
+ sb.append(", mOrder="); sb.append(mOrder);
sb.append(", mHasPartialTypes="); sb.append(mHasPartialTypes);
du.println(sb.toString());
}
@@ -1888,6 +1952,7 @@ public class IntentFilter implements Parcelable {
dest.writeInt(mHasPartialTypes ? 1 : 0);
dest.writeInt(getAutoVerify() ? 1 : 0);
dest.writeInt(mInstantAppVisibility);
+ dest.writeInt(mOrder);
}
/**
@@ -1957,6 +2022,7 @@ public class IntentFilter implements Parcelable {
mHasPartialTypes = source.readInt() > 0;
setAutoVerify(source.readInt() > 0);
setVisibilityToInstantApp(source.readInt());
+ mOrder = source.readInt();
}
private final boolean findMimeType(String type) {
diff --git a/core/java/android/content/Loader.java b/core/java/android/content/Loader.java
index 3faf13b601ad..b0555d4ce7e0 100644
--- a/core/java/android/content/Loader.java
+++ b/core/java/android/content/Loader.java
@@ -48,7 +48,11 @@ import java.io.PrintWriter;
* </div>
*
* @param <D> The result returned when the load is complete
+ *
+ * @deprecated Use the <a href="{@docRoot}tools/extras/support-library.html">Support Library</a>
+ * {@link android.support.v4.content.Loader}
*/
+@Deprecated
public class Loader<D> {
int mId;
OnLoadCompleteListener<D> mListener;
@@ -66,7 +70,10 @@ public class Loader<D> {
* is told it has changed. You do not normally need to use this yourself;
* it is used for you by {@link CursorLoader} to take care of executing
* an update when the cursor's backing data changes.
+ *
+ * @deprecated Use {@link android.support.v4.content.Loader.ForceLoadContentObserver}
*/
+ @Deprecated
public final class ForceLoadContentObserver extends ContentObserver {
public ForceLoadContentObserver() {
super(new Handler());
@@ -90,7 +97,10 @@ public class Loader<D> {
* to find out when a Loader it is managing has completed so that this can
* be reported to its client. This interface should only be used if a
* Loader is not being used in conjunction with LoaderManager.
+ *
+ * @deprecated Use {@link android.support.v4.content.Loader.OnLoadCompleteListener}
*/
+ @Deprecated
public interface OnLoadCompleteListener<D> {
/**
* Called on the thread that created the Loader when the load is complete.
@@ -108,7 +118,10 @@ public class Loader<D> {
* to find out when a Loader it is managing has been canceled so that it
* can schedule the next Loader. This interface should only be used if a
* Loader is not being used in conjunction with LoaderManager.
+ *
+ * @deprecated Use {@link android.support.v4.content.Loader.OnLoadCanceledListener}
*/
+ @Deprecated
public interface OnLoadCanceledListener<D> {
/**
* Called on the thread that created the Loader when the load is canceled.
@@ -549,4 +562,4 @@ public class Loader<D> {
writer.print(" mReset="); writer.println(mReset);
}
}
-} \ No newline at end of file
+}
diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java
new file mode 100644
index 000000000000..9f5c877e7081
--- /dev/null
+++ b/core/java/android/content/PermissionChecker.java
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+
+package android.content;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Process;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class provides permission check APIs that verify both the
+ * permission and the associated app op for this permission if
+ * such is defined.
+ * <p>
+ * In the new permission model permissions with protection level
+ * dangerous are runtime permissions. For apps targeting {@link android.os.Build.VERSION_CODES#M}
+ * and above the user may not grant such permissions or revoke
+ * them at any time. For apps targeting API lower than {@link android.os.Build.VERSION_CODES#M}
+ * these permissions are always granted as such apps do not expect
+ * permission revocations and would crash. Therefore, when the
+ * user disables a permission for a legacy app in the UI the
+ * platform disables the APIs guarded by this permission making
+ * them a no-op which is doing nothing or returning an empty
+ * result or default error.
+ * </p>
+ * <p>
+ * It is important that when you perform an operation on behalf of
+ * another app you use these APIs to check for permissions as the
+ * app may be a legacy app that does not participate in the new
+ * permission model for which the user had disabled the "permission"
+ * which is achieved by disallowing the corresponding app op.
+ * </p>
+ *
+ * @hide
+ */
+public final class PermissionChecker {
+ /** Permission result: The permission is granted. */
+ public static final int PERMISSION_GRANTED = PackageManager.PERMISSION_GRANTED;
+
+ /** Permission result: The permission is denied. */
+ public static final int PERMISSION_DENIED = PackageManager.PERMISSION_DENIED;
+
+ /** Permission result: The permission is denied because the app op is not allowed. */
+ public static final int PERMISSION_DENIED_APP_OP = PackageManager.PERMISSION_DENIED - 1;
+
+ /** @hide */
+ @IntDef({PERMISSION_GRANTED,
+ PERMISSION_DENIED,
+ PERMISSION_DENIED_APP_OP})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PermissionResult {}
+
+ private PermissionChecker() {
+ /* do nothing */
+ }
+
+ /**
+ * Checks whether a given package in a UID and PID has a given permission
+ * and whether the app op that corresponds to this permission is allowed.
+ *
+ * @param context Context for accessing resources.
+ * @param permission The permission to check.
+ * @param pid The process id for which to check.
+ * @param uid The uid for which to check.
+ * @param packageName The package name for which to check. If null the
+ * the first package for the calling UID will be used.
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+ */
+ @PermissionResult
+ public static int checkPermission(@NonNull Context context, @NonNull String permission,
+ int pid, int uid, @Nullable String packageName) {
+ if (context.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_DENIED) {
+ return PERMISSION_DENIED;
+ }
+
+ AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
+ String op = appOpsManager.permissionToOp(permission);
+ if (op == null) {
+ return PERMISSION_GRANTED;
+ }
+
+ if (packageName == null) {
+ String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
+ if (packageNames == null || packageNames.length <= 0) {
+ return PERMISSION_DENIED;
+ }
+ packageName = packageNames[0];
+ }
+
+ if (appOpsManager.noteProxyOpNoThrow(op, packageName)
+ != AppOpsManager.MODE_ALLOWED) {
+ return PERMISSION_DENIED_APP_OP;
+ }
+
+ return PERMISSION_GRANTED;
+ }
+
+ /**
+ * Checks whether your app has a given permission and whether the app op
+ * that corresponds to this permission is allowed.
+ *
+ * @param context Context for accessing resources.
+ * @param permission The permission to check.
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+ */
+ @PermissionResult
+ public static int checkSelfPermission(@NonNull Context context,
+ @NonNull String permission) {
+ return checkPermission(context, permission, Process.myPid(),
+ Process.myUid(), context.getPackageName());
+ }
+
+ /**
+ * Checks whether the IPC you are handling has a given permission and whether
+ * the app op that corresponds to this permission is allowed.
+ *
+ * @param context Context for accessing resources.
+ * @param permission The permission to check.
+ * @param packageName The package name making the IPC. If null the
+ * the first package for the calling UID will be used.
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+ */
+ @PermissionResult
+ public static int checkCallingPermission(@NonNull Context context,
+ @NonNull String permission, @Nullable String packageName) {
+ if (Binder.getCallingPid() == Process.myPid()) {
+ return PERMISSION_DENIED;
+ }
+ return checkPermission(context, permission, Binder.getCallingPid(),
+ Binder.getCallingUid(), packageName);
+ }
+
+ /**
+ * Checks whether the IPC you are handling or your app has a given permission
+ * and whether the app op that corresponds to this permission is allowed.
+ *
+ * @param context Context for accessing resources.
+ * @param permission The permission to check.
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+ */
+ @PermissionResult
+ public static int checkCallingOrSelfPermission(@NonNull Context context,
+ @NonNull String permission) {
+ String packageName = (Binder.getCallingPid() == Process.myPid())
+ ? context.getPackageName() : null;
+ return checkPermission(context, permission, Binder.getCallingPid(),
+ Binder.getCallingUid(), packageName);
+ }
+}
diff --git a/core/java/android/content/QuickViewConstants.java b/core/java/android/content/QuickViewConstants.java
index 7455d0cb0944..132d43f2e143 100644
--- a/core/java/android/content/QuickViewConstants.java
+++ b/core/java/android/content/QuickViewConstants.java
@@ -33,7 +33,7 @@ public class QuickViewConstants {
public static final String FEATURE_VIEW = "android:view";
/**
- * Feature to view a document using system standard editing mechanism, like
+ * Feature to edit a document using system standard editing mechanism, like
* {@link Intent#ACTION_EDIT}.
*
* @see Intent#EXTRA_QUICK_VIEW_FEATURES
@@ -42,6 +42,15 @@ public class QuickViewConstants {
public static final String FEATURE_EDIT = "android:edit";
/**
+ * Feature to delete an individual document. Quick viewer implementations must use
+ * Storage Access Framework to both verify delete permission and to delete content.
+ *
+ * @see DocumentsContract.Document#FLAG_SUPPORTS_DELETE
+ * @see DocumentsContract#deleteDocument(ContentResolver, Uri)
+ */
+ public static final String FEATURE_DELETE = "android:delete";
+
+ /**
* Feature to view a document using system standard sending mechanism, like
* {@link Intent#ACTION_SEND}.
*
diff --git a/core/java/android/content/ServiceConnection.java b/core/java/android/content/ServiceConnection.java
index 6ff4900212ff..21398f6e6473 100644
--- a/core/java/android/content/ServiceConnection.java
+++ b/core/java/android/content/ServiceConnection.java
@@ -31,6 +31,11 @@ public interface ServiceConnection {
* the {@link android.os.IBinder} of the communication channel to the
* Service.
*
+ * <p class="note"><b>Note:</b> If the system has started to bind your
+ * client app to a service, it's possible that your app will never receive
+ * this callback. Your app won't receive a callback if there's an issue with
+ * the service, such as the service crashing while being created.
+ *
* @param name The concrete component name of the service that has
* been connected.
*
@@ -63,4 +68,21 @@ public interface ServiceConnection {
*/
default void onBindingDied(ComponentName name) {
}
+
+ /**
+ * Called when the service being bound has returned {@code null} from its
+ * {@link android.app.Service#onBind(Intent) onBind()} method. This indicates
+ * that the attempting service binding represented by this ServiceConnection
+ * will never become usable.
+ *
+ * <p class="note">The app which requested the binding must still call
+ * {@link Context#unbindService(ServiceConnection)} to release the tracking
+ * resources associated with this ServiceConnection even if this callback was
+ * invoked following {@link Context#bindService Context.bindService() bindService()}.
+ *
+ * @param name The concrete component name of the service whose binding
+ * has been rejected by the Service implementation.
+ */
+ default void onNullBinding(ComponentName name) {
+ }
}
diff --git a/core/java/android/content/SharedPreferences.java b/core/java/android/content/SharedPreferences.java
index 4b09feda2173..d3652e8825b5 100644
--- a/core/java/android/content/SharedPreferences.java
+++ b/core/java/android/content/SharedPreferences.java
@@ -30,6 +30,11 @@ import java.util.Set;
* when they are committed to storage. Objects that are returned from the
* various <code>get</code> methods must be treated as immutable by the application.
*
+ * <p>Note: This class provides strong consistency guarantees. It is using expensive operations
+ * which might slow down an app. Frequently changing properties or properties where loss can be
+ * tolerated should use other mechanisms. For more details read the comments on
+ * {@link Editor#commit()} and {@link Editor#apply()}.
+ *
* <p><em>Note: This class does not support use across multiple processes.</em>
*
* <div class="special reference">
diff --git a/core/java/android/content/SyncResult.java b/core/java/android/content/SyncResult.java
index 4f86af985dc6..f67d7f53d1c1 100644
--- a/core/java/android/content/SyncResult.java
+++ b/core/java/android/content/SyncResult.java
@@ -79,7 +79,17 @@ public final class SyncResult implements Parcelable {
/**
* Used to indicate to the SyncManager that future sync requests that match the request's
- * Account and authority should be delayed at least this many seconds.
+ * Account and authority should be delayed until a time in seconds since Java epoch.
+ *
+ * <p>For example, if you want to delay the next sync for at least 5 minutes, then:
+ * <pre>
+ * result.delayUntil = (System.currentTimeMillis() / 1000) + 5 * 60;
+ * </pre>
+ *
+ * <p>By default, when a sync fails, the system retries later with an exponential back-off
+ * with the system default initial delay time, which always wins over {@link #delayUntil} --
+ * i.e. if the system back-off time is larger than {@link #delayUntil}, {@link #delayUntil}
+ * will essentially be ignored.
*/
public long delayUntil;
diff --git a/core/java/android/content/SyncStatusInfo.java b/core/java/android/content/SyncStatusInfo.java
index abf9cc91dbda..2d521e9e4e60 100644
--- a/core/java/android/content/SyncStatusInfo.java
+++ b/core/java/android/content/SyncStatusInfo.java
@@ -21,23 +21,102 @@ import android.os.Parcelable;
import android.util.Log;
import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
/** @hide */
public class SyncStatusInfo implements Parcelable {
private static final String TAG = "Sync";
- static final int VERSION = 4;
+ static final int VERSION = 6;
private static final int MAX_EVENT_COUNT = 10;
+ /**
+ * Number of sync sources. KEEP THIS AND SyncStorageEngine.SOURCES IN SYNC.
+ */
+ private static final int SOURCE_COUNT = 6;
+
public final int authorityId;
- public long totalElapsedTime;
- public int numSyncs;
- public int numSourcePoll;
- public int numSourceServer;
- public int numSourceLocal;
- public int numSourceUser;
- public int numSourcePeriodic;
+
+ /**
+ * # of syncs for each sync source, etc.
+ */
+ public static class Stats {
+ public long totalElapsedTime;
+ public int numSyncs;
+ public int numSourcePoll;
+ public int numSourceOther;
+ public int numSourceLocal;
+ public int numSourceUser;
+ public int numSourcePeriodic;
+ public int numSourceFeed;
+ public int numFailures;
+ public int numCancels;
+
+ /** Copy all the stats to another instance. */
+ public void copyTo(Stats to) {
+ to.totalElapsedTime = totalElapsedTime;
+ to.numSyncs = numSyncs;
+ to.numSourcePoll = numSourcePoll;
+ to.numSourceOther = numSourceOther;
+ to.numSourceLocal = numSourceLocal;
+ to.numSourceUser = numSourceUser;
+ to.numSourcePeriodic = numSourcePeriodic;
+ to.numSourceFeed = numSourceFeed;
+ to.numFailures = numFailures;
+ to.numCancels = numCancels;
+ }
+
+ /** Clear all the stats. */
+ public void clear() {
+ totalElapsedTime = 0;
+ numSyncs = 0;
+ numSourcePoll = 0;
+ numSourceOther = 0;
+ numSourceLocal = 0;
+ numSourceUser = 0;
+ numSourcePeriodic = 0;
+ numSourceFeed = 0;
+ numFailures = 0;
+ numCancels = 0;
+ }
+
+ /** Write all the stats to a parcel. */
+ public void writeToParcel(Parcel parcel) {
+ parcel.writeLong(totalElapsedTime);
+ parcel.writeInt(numSyncs);
+ parcel.writeInt(numSourcePoll);
+ parcel.writeInt(numSourceOther);
+ parcel.writeInt(numSourceLocal);
+ parcel.writeInt(numSourceUser);
+ parcel.writeInt(numSourcePeriodic);
+ parcel.writeInt(numSourceFeed);
+ parcel.writeInt(numFailures);
+ parcel.writeInt(numCancels);
+ }
+
+ /** Read all the stats from a parcel. */
+ public void readFromParcel(Parcel parcel) {
+ totalElapsedTime = parcel.readLong();
+ numSyncs = parcel.readInt();
+ numSourcePoll = parcel.readInt();
+ numSourceOther = parcel.readInt();
+ numSourceLocal = parcel.readInt();
+ numSourceUser = parcel.readInt();
+ numSourcePeriodic = parcel.readInt();
+ numSourceFeed = parcel.readInt();
+ numFailures = parcel.readInt();
+ numCancels = parcel.readInt();
+ }
+ }
+
+ public long lastTodayResetTime;
+
+ public final Stats totalStats = new Stats();
+ public final Stats todayStats = new Stats();
+ public final Stats yesterdayStats = new Stats();
+
public long lastSuccessTime;
public int lastSuccessSource;
public long lastFailureTime;
@@ -46,7 +125,10 @@ public class SyncStatusInfo implements Parcelable {
public long initialFailureTime;
public boolean pending;
public boolean initialize;
-
+
+ public final long[] perSourceLastSuccessTimes = new long[SOURCE_COUNT];
+ public final long[] perSourceLastFailureTimes = new long[SOURCE_COUNT];
+
// Warning: It is up to the external caller to ensure there are
// no race conditions when accessing this list
private ArrayList<Long> periodicSyncTimes;
@@ -75,12 +157,15 @@ public class SyncStatusInfo implements Parcelable {
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(VERSION);
parcel.writeInt(authorityId);
- parcel.writeLong(totalElapsedTime);
- parcel.writeInt(numSyncs);
- parcel.writeInt(numSourcePoll);
- parcel.writeInt(numSourceServer);
- parcel.writeInt(numSourceLocal);
- parcel.writeInt(numSourceUser);
+
+ // Note we can't use Stats.writeToParcel() here; see the below constructor for the reason.
+ parcel.writeLong(totalStats.totalElapsedTime);
+ parcel.writeInt(totalStats.numSyncs);
+ parcel.writeInt(totalStats.numSourcePoll);
+ parcel.writeInt(totalStats.numSourceOther);
+ parcel.writeInt(totalStats.numSourceLocal);
+ parcel.writeInt(totalStats.numSourceUser);
+
parcel.writeLong(lastSuccessTime);
parcel.writeInt(lastSuccessSource);
parcel.writeLong(lastFailureTime);
@@ -102,7 +187,22 @@ public class SyncStatusInfo implements Parcelable {
parcel.writeLong(mLastEventTimes.get(i));
parcel.writeString(mLastEvents.get(i));
}
- parcel.writeInt(numSourcePeriodic);
+ // Version 4
+ parcel.writeInt(totalStats.numSourcePeriodic);
+
+ // Version 5
+ parcel.writeInt(totalStats.numSourceFeed);
+ parcel.writeInt(totalStats.numFailures);
+ parcel.writeInt(totalStats.numCancels);
+
+ parcel.writeLong(lastTodayResetTime);
+
+ todayStats.writeToParcel(parcel);
+ yesterdayStats.writeToParcel(parcel);
+
+ // Version 6.
+ parcel.writeLongArray(perSourceLastSuccessTimes);
+ parcel.writeLongArray(perSourceLastFailureTimes);
}
public SyncStatusInfo(Parcel parcel) {
@@ -111,12 +211,15 @@ public class SyncStatusInfo implements Parcelable {
Log.w("SyncStatusInfo", "Unknown version: " + version);
}
authorityId = parcel.readInt();
- totalElapsedTime = parcel.readLong();
- numSyncs = parcel.readInt();
- numSourcePoll = parcel.readInt();
- numSourceServer = parcel.readInt();
- numSourceLocal = parcel.readInt();
- numSourceUser = parcel.readInt();
+
+ // Note we can't use Stats.writeToParcel() here because the data is persisted and we need
+ // to be able to read from the old format too.
+ totalStats.totalElapsedTime = parcel.readLong();
+ totalStats.numSyncs = parcel.readInt();
+ totalStats.numSourcePoll = parcel.readInt();
+ totalStats.numSourceOther = parcel.readInt();
+ totalStats.numSourceLocal = parcel.readInt();
+ totalStats.numSourceUser = parcel.readInt();
lastSuccessTime = parcel.readLong();
lastSuccessSource = parcel.readInt();
lastFailureTime = parcel.readLong();
@@ -149,25 +252,41 @@ public class SyncStatusInfo implements Parcelable {
}
if (version < 4) {
// Before version 4, numSourcePeriodic wasn't persisted.
- numSourcePeriodic = numSyncs - numSourceLocal - numSourcePoll - numSourceServer
- - numSourceUser;
- if (numSourcePeriodic < 0) { // Sanity check.
- numSourcePeriodic = 0;
+ totalStats.numSourcePeriodic =
+ totalStats.numSyncs - totalStats.numSourceLocal - totalStats.numSourcePoll
+ - totalStats.numSourceOther
+ - totalStats.numSourceUser;
+ if (totalStats.numSourcePeriodic < 0) { // Sanity check.
+ totalStats.numSourcePeriodic = 0;
}
} else {
- numSourcePeriodic = parcel.readInt();
+ totalStats.numSourcePeriodic = parcel.readInt();
+ }
+ if (version >= 5) {
+ totalStats.numSourceFeed = parcel.readInt();
+ totalStats.numFailures = parcel.readInt();
+ totalStats.numCancels = parcel.readInt();
+
+ lastTodayResetTime = parcel.readLong();
+
+ todayStats.readFromParcel(parcel);
+ yesterdayStats.readFromParcel(parcel);
+ }
+ if (version >= 6) {
+ parcel.readLongArray(perSourceLastSuccessTimes);
+ parcel.readLongArray(perSourceLastFailureTimes);
}
}
public SyncStatusInfo(SyncStatusInfo other) {
authorityId = other.authorityId;
- totalElapsedTime = other.totalElapsedTime;
- numSyncs = other.numSyncs;
- numSourcePoll = other.numSourcePoll;
- numSourceServer = other.numSourceServer;
- numSourceLocal = other.numSourceLocal;
- numSourceUser = other.numSourceUser;
- numSourcePeriodic = other.numSourcePeriodic;
+
+ other.totalStats.copyTo(totalStats);
+ other.todayStats.copyTo(todayStats);
+ other.yesterdayStats.copyTo(yesterdayStats);
+
+ lastTodayResetTime = other.lastTodayResetTime;
+
lastSuccessTime = other.lastSuccessTime;
lastSuccessSource = other.lastSuccessSource;
lastFailureTime = other.lastFailureTime;
@@ -181,6 +300,13 @@ public class SyncStatusInfo implements Parcelable {
}
mLastEventTimes.addAll(other.mLastEventTimes);
mLastEvents.addAll(other.mLastEvents);
+
+ copy(perSourceLastSuccessTimes, other.perSourceLastSuccessTimes);
+ copy(perSourceLastFailureTimes, other.perSourceLastFailureTimes);
+ }
+
+ private static void copy(long[] to, long[] from) {
+ System.arraycopy(from, 0, to, 0, to.length);
}
public void setPeriodicSyncTime(int index, long when) {
@@ -229,6 +355,34 @@ public class SyncStatusInfo implements Parcelable {
return mLastEvents.get(i);
}
+ /** Call this when a sync has succeeded. */
+ public void setLastSuccess(int source, long lastSyncTime) {
+ lastSuccessTime = lastSyncTime;
+ lastSuccessSource = source;
+ lastFailureTime = 0;
+ lastFailureSource = -1;
+ lastFailureMesg = null;
+ initialFailureTime = 0;
+
+ if (0 <= source && source < perSourceLastSuccessTimes.length) {
+ perSourceLastSuccessTimes[source] = lastSyncTime;
+ }
+ }
+
+ /** Call this when a sync has failed. */
+ public void setLastFailure(int source, long lastSyncTime, String failureMessage) {
+ lastFailureTime = lastSyncTime;
+ lastFailureSource = source;
+ lastFailureMesg = failureMessage;
+ if (initialFailureTime == 0) {
+ initialFailureTime = lastSyncTime;
+ }
+
+ if (0 <= source && source < perSourceLastFailureTimes.length) {
+ perSourceLastFailureTimes[source] = lastSyncTime;
+ }
+ }
+
public static final Creator<SyncStatusInfo> CREATOR = new Creator<SyncStatusInfo>() {
public SyncStatusInfo createFromParcel(Parcel in) {
return new SyncStatusInfo(in);
@@ -251,4 +405,41 @@ public class SyncStatusInfo implements Parcelable {
}
}
}
-} \ No newline at end of file
+
+ /**
+ * If the last reset was not today, move today's stats to yesterday's and clear today's.
+ */
+ public void maybeResetTodayStats(boolean clockValid, boolean force) {
+ final long now = System.currentTimeMillis();
+
+ if (!force) {
+ // Last reset was the same day, nothing to do.
+ if (areSameDates(now, lastTodayResetTime)) {
+ return;
+ }
+
+ // Hack -- on devices with no RTC, until the NTP kicks in, the device won't have the
+ // correct time. So if the time goes back, don't reset, unless we're sure the current
+ // time is correct.
+ if (now < lastTodayResetTime && !clockValid) {
+ return;
+ }
+ }
+
+ lastTodayResetTime = now;
+
+ todayStats.copyTo(yesterdayStats);
+ todayStats.clear();
+ }
+
+ private static boolean areSameDates(long time1, long time2) {
+ final Calendar c1 = new GregorianCalendar();
+ final Calendar c2 = new GregorianCalendar();
+
+ c1.setTimeInMillis(time1);
+ c2.setTimeInMillis(time2);
+
+ return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR)
+ && c1.get(Calendar.DAY_OF_YEAR) == c2.get(Calendar.DAY_OF_YEAR);
+ }
+}
diff --git a/core/java/android/content/om/IOverlayManager.aidl b/core/java/android/content/om/IOverlayManager.aidl
index 86c1aa8228f1..5b3c9dd93370 100644
--- a/core/java/android/content/om/IOverlayManager.aidl
+++ b/core/java/android/content/om/IOverlayManager.aidl
@@ -83,17 +83,36 @@ interface IOverlayManager {
* @param packageName The name of the overlay package.
* @param enable true to enable the overlay, false to disable it.
* @param userId The user for which to change the overlay.
- * @return true if the system successfully registered the request, false
- * otherwise.
+ * @return true if the system successfully registered the request, false otherwise.
*/
boolean setEnabled(in String packageName, in boolean enable, in int userId);
/**
- * Version of setEnabled that will also disable any other overlays for the target package.
+ * Request that an overlay package is enabled and any other overlay packages with the same
+ * target package are disabled.
+ *
+ * See {@link #setEnabled} for the details on overlay packages.
+ *
+ * @param packageName the name of the overlay package to enable.
+ * @param enabled must be true, otherwise the operation fails.
+ * @param userId The user for which to change the overlay.
+ * @return true if the system successfully registered the request, false otherwise.
*/
boolean setEnabledExclusive(in String packageName, in boolean enable, in int userId);
/**
+ * Request that an overlay package is enabled and any other overlay packages with the same
+ * target package and category are disabled.
+ *
+ * See {@link #setEnabled} for the details on overlay packages.
+ *
+ * @param packageName the name of the overlay package to enable.
+ * @param userId The user for which to change the overlay.
+ * @return true if the system successfully registered the request, false otherwise.
+ */
+ boolean setEnabledExclusiveInCategory(in String packageName, in int userId);
+
+ /**
* Change the priority of the given overlay to be just higher than the
* overlay with package name newParentPackageName. Both overlay packages
* must have the same target and user.
diff --git a/core/java/android/content/om/OverlayInfo.java b/core/java/android/content/om/OverlayInfo.java
index 1a207ba055fc..edacbb0bb2b4 100644
--- a/core/java/android/content/om/OverlayInfo.java
+++ b/core/java/android/content/om/OverlayInfo.java
@@ -16,10 +16,14 @@
package android.content.om;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Immutable overlay information about a package. All PackageInfos that
* represent an overlay package will have a corresponding OverlayInfo.
@@ -27,6 +31,20 @@ import android.os.Parcelable;
* @hide
*/
public final class OverlayInfo implements Parcelable {
+
+ @IntDef(prefix = "STATE_", value = {
+ STATE_UNKNOWN,
+ STATE_MISSING_TARGET,
+ STATE_NO_IDMAP,
+ STATE_DISABLED,
+ STATE_ENABLED,
+ STATE_ENABLED_STATIC,
+ STATE_TARGET_UPGRADING,
+ STATE_OVERLAY_UPGRADING,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface State {}
+
/**
* An internal state used as the initial state of an overlay. OverlayInfo
* objects exposed outside the {@link
@@ -49,18 +67,44 @@ public final class OverlayInfo implements Parcelable {
/**
* The overlay is currently disabled. It can be enabled.
*
- * @see IOverlayManager.setEnabled
+ * @see IOverlayManager#setEnabled
*/
public static final int STATE_DISABLED = 2;
/**
* The overlay is currently enabled. It can be disabled.
*
- * @see IOverlayManager.setEnabled
+ * @see IOverlayManager#setEnabled
*/
public static final int STATE_ENABLED = 3;
/**
+ * The target package is currently being upgraded; the state will change
+ * once the package installation has finished.
+ */
+ public static final int STATE_TARGET_UPGRADING = 4;
+
+ /**
+ * The overlay package is currently being upgraded; the state will change
+ * once the package installation has finished.
+ */
+ public static final int STATE_OVERLAY_UPGRADING = 5;
+
+ /**
+ * The overlay package is currently enabled because it is marked as
+ * 'static'. It cannot be disabled but will change state if for instance
+ * its target is uninstalled.
+ */
+ public static final int STATE_ENABLED_STATIC = 6;
+
+ /**
+ * Overlay category: theme.
+ * <p>
+ * Change how Android (including the status bar, dialogs, ...) looks.
+ */
+ public static final String CATEGORY_THEME = "android.theme";
+
+ /**
* Package name of the overlay package
*/
public final String packageName;
@@ -71,19 +115,19 @@ public final class OverlayInfo implements Parcelable {
public final String targetPackageName;
/**
+ * Category of the overlay package
+ */
+ public final String category;
+
+ /**
* Full path to the base APK for this overlay package
*/
public final String baseCodePath;
/**
* The state of this OverlayInfo as defined by the STATE_* constants in this class.
- *
- * @see #STATE_MISSING_TARGET
- * @see #STATE_NO_IDMAP
- * @see #STATE_DISABLED
- * @see #STATE_ENABLED
*/
- public final int state;
+ public final @State int state;
/**
* User handle for which this overlay applies
@@ -91,32 +135,56 @@ public final class OverlayInfo implements Parcelable {
public final int userId;
/**
+ * Priority as read from the manifest. Used if isStatic is true. Not
+ * intended to be exposed to 3rd party.
+ *
+ * @hide
+ */
+ public final int priority;
+
+ /**
+ * isStatic as read from the manifest. If true, the overlay is
+ * unconditionally loaded and cannot be unloaded. Not intended to be
+ * exposed to 3rd party.
+ *
+ * @hide
+ */
+ public final boolean isStatic;
+
+ /**
* Create a new OverlayInfo based on source with an updated state.
*
* @param source the source OverlayInfo to base the new instance on
* @param state the new state for the source OverlayInfo
*/
- public OverlayInfo(@NonNull OverlayInfo source, int state) {
- this(source.packageName, source.targetPackageName, source.baseCodePath, state,
- source.userId);
+ public OverlayInfo(@NonNull OverlayInfo source, @State int state) {
+ this(source.packageName, source.targetPackageName, source.category, source.baseCodePath,
+ state, source.userId, source.priority, source.isStatic);
}
public OverlayInfo(@NonNull String packageName, @NonNull String targetPackageName,
- @NonNull String baseCodePath, int state, int userId) {
+ @NonNull String category, @NonNull String baseCodePath, int state, int userId,
+ int priority, boolean isStatic) {
this.packageName = packageName;
this.targetPackageName = targetPackageName;
+ this.category = category;
this.baseCodePath = baseCodePath;
this.state = state;
this.userId = userId;
+ this.priority = priority;
+ this.isStatic = isStatic;
ensureValidState();
}
public OverlayInfo(Parcel source) {
packageName = source.readString();
targetPackageName = source.readString();
+ category = source.readString();
baseCodePath = source.readString();
state = source.readInt();
userId = source.readInt();
+ priority = source.readInt();
+ isStatic = source.readBoolean();
ensureValidState();
}
@@ -136,6 +204,9 @@ public final class OverlayInfo implements Parcelable {
case STATE_NO_IDMAP:
case STATE_DISABLED:
case STATE_ENABLED:
+ case STATE_ENABLED_STATIC:
+ case STATE_TARGET_UPGRADING:
+ case STATE_OVERLAY_UPGRADING:
break;
default:
throw new IllegalArgumentException("State " + state + " is not a valid state");
@@ -151,12 +222,16 @@ public final class OverlayInfo implements Parcelable {
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(packageName);
dest.writeString(targetPackageName);
+ dest.writeString(category);
dest.writeString(baseCodePath);
dest.writeInt(state);
dest.writeInt(userId);
+ dest.writeInt(priority);
+ dest.writeBoolean(isStatic);
}
- public static final Parcelable.Creator<OverlayInfo> CREATOR = new Parcelable.Creator<OverlayInfo>() {
+ public static final Parcelable.Creator<OverlayInfo> CREATOR =
+ new Parcelable.Creator<OverlayInfo>() {
@Override
public OverlayInfo createFromParcel(Parcel source) {
return new OverlayInfo(source);
@@ -179,6 +254,7 @@ public final class OverlayInfo implements Parcelable {
public boolean isEnabled() {
switch (state) {
case STATE_ENABLED:
+ case STATE_ENABLED_STATIC:
return true;
default:
return false;
@@ -189,14 +265,9 @@ public final class OverlayInfo implements Parcelable {
* Translate a state to a human readable string. Only intended for
* debugging purposes.
*
- * @see #STATE_MISSING_TARGET
- * @see #STATE_NO_IDMAP
- * @see #STATE_DISABLED
- * @see #STATE_ENABLED
- *
* @return a human readable String representing the state.
*/
- public static String stateToString(int state) {
+ public static String stateToString(@State int state) {
switch (state) {
case STATE_UNKNOWN:
return "STATE_UNKNOWN";
@@ -208,6 +279,12 @@ public final class OverlayInfo implements Parcelable {
return "STATE_DISABLED";
case STATE_ENABLED:
return "STATE_ENABLED";
+ case STATE_ENABLED_STATIC:
+ return "STATE_ENABLED_STATIC";
+ case STATE_TARGET_UPGRADING:
+ return "STATE_TARGET_UPGRADING";
+ case STATE_OVERLAY_UPGRADING:
+ return "STATE_OVERLAY_UPGRADING";
default:
return "<unknown state>";
}
@@ -221,6 +298,7 @@ public final class OverlayInfo implements Parcelable {
result = prime * result + state;
result = prime * result + ((packageName == null) ? 0 : packageName.hashCode());
result = prime * result + ((targetPackageName == null) ? 0 : targetPackageName.hashCode());
+ result = prime * result + ((category == null) ? 0 : category.hashCode());
result = prime * result + ((baseCodePath == null) ? 0 : baseCodePath.hashCode());
return result;
}
@@ -249,6 +327,9 @@ public final class OverlayInfo implements Parcelable {
if (!targetPackageName.equals(other.targetPackageName)) {
return false;
}
+ if (!category.equals(other.category)) {
+ return false;
+ }
if (!baseCodePath.equals(other.baseCodePath)) {
return false;
}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 93338bb5b025..0e91a2927c79 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -17,6 +17,7 @@
package android.content.pm;
import android.annotation.IntDef;
+import android.annotation.TestApi;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Configuration.NativeConfig;
@@ -34,8 +35,7 @@ import java.lang.annotation.RetentionPolicy;
* from the AndroidManifest.xml's &lt;activity&gt; and
* &lt;receiver&gt; tags.
*/
-public class ActivityInfo extends ComponentInfo
- implements Parcelable {
+public class ActivityInfo extends ComponentInfo implements Parcelable {
// NOTE: When adding new data members be sure to update the copy-constructor, Parcel
// constructor, and writeToParcel.
@@ -181,6 +181,7 @@ public class ActivityInfo extends ComponentInfo
* Activity explicitly requested to be resizeable.
* @hide
*/
+ @TestApi
public static final int RESIZE_MODE_RESIZEABLE = 2;
/**
* Activity is resizeable and supported picture-in-picture mode. This flag is now deprecated
@@ -261,10 +262,10 @@ public class ActivityInfo extends ComponentInfo
public static final int COLOR_MODE_HDR = 2;
/** @hide */
- @IntDef({
- COLOR_MODE_DEFAULT,
- COLOR_MODE_WIDE_COLOR_GAMUT,
- COLOR_MODE_HDR,
+ @IntDef(prefix = { "COLOR_MODE_" }, value = {
+ COLOR_MODE_DEFAULT,
+ COLOR_MODE_WIDE_COLOR_GAMUT,
+ COLOR_MODE_HDR,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ColorMode {}
@@ -454,7 +455,6 @@ public class ActivityInfo extends ComponentInfo
*/
public static final int FLAG_TURN_SCREEN_ON = 0x1000000;
-
/**
* @hide Bit in {@link #flags}: If set, this component will only be seen
* by the system user. Only works with broadcast receivers. Set from the
@@ -492,7 +492,7 @@ public class ActivityInfo extends ComponentInfo
public int flags;
/** @hide */
- @IntDef({
+ @IntDef(prefix = { "SCREEN_ORIENTATION_" }, value = {
SCREEN_ORIENTATION_UNSET,
SCREEN_ORIENTATION_UNSPECIFIED,
SCREEN_ORIENTATION_LANDSCAPE,
@@ -638,25 +638,24 @@ public class ActivityInfo extends ComponentInfo
public int screenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
/** @hide */
- @IntDef(flag = true,
- value = {
- CONFIG_MCC,
- CONFIG_MNC,
- CONFIG_LOCALE,
- CONFIG_TOUCHSCREEN,
- CONFIG_KEYBOARD,
- CONFIG_KEYBOARD_HIDDEN,
- CONFIG_NAVIGATION,
- CONFIG_ORIENTATION,
- CONFIG_SCREEN_LAYOUT,
- CONFIG_UI_MODE,
- CONFIG_SCREEN_SIZE,
- CONFIG_SMALLEST_SCREEN_SIZE,
- CONFIG_DENSITY,
- CONFIG_LAYOUT_DIRECTION,
- CONFIG_COLOR_MODE,
- CONFIG_FONT_SCALE,
- })
+ @IntDef(flag = true, prefix = { "CONFIG_" }, value = {
+ CONFIG_MCC,
+ CONFIG_MNC,
+ CONFIG_LOCALE,
+ CONFIG_TOUCHSCREEN,
+ CONFIG_KEYBOARD,
+ CONFIG_KEYBOARD_HIDDEN,
+ CONFIG_NAVIGATION,
+ CONFIG_ORIENTATION,
+ CONFIG_SCREEN_LAYOUT,
+ CONFIG_UI_MODE,
+ CONFIG_SCREEN_SIZE,
+ CONFIG_SMALLEST_SCREEN_SIZE,
+ CONFIG_DENSITY,
+ CONFIG_LAYOUT_DIRECTION,
+ CONFIG_COLOR_MODE,
+ CONFIG_FONT_SCALE,
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface Config {}
@@ -780,6 +779,13 @@ public class ActivityInfo extends ComponentInfo
* constant starts at the high bits.
*/
public static final int CONFIG_FONT_SCALE = 0x40000000;
+ /**
+ * Bit indicating changes to window configuration that isn't exposed to apps.
+ * This is for internal use only and apps don't handle it.
+ * @hide
+ * {@link Configuration}.
+ */
+ public static final int CONFIG_WINDOW_CONFIGURATION = 0x20000000;
/** @hide
* Unfortunately the constants for config changes in native code are
@@ -993,20 +999,12 @@ public class ActivityInfo extends ComponentInfo
* Returns true if the activity's orientation is fixed.
* @hide
*/
- public boolean isFixedOrientation() {
+ boolean isFixedOrientation() {
return isFixedOrientationLandscape() || isFixedOrientationPortrait()
|| screenOrientation == SCREEN_ORIENTATION_LOCKED;
}
/**
- * Returns true if the specified orientation is considered fixed.
- * @hide
- */
- static public boolean isFixedOrientation(int orientation) {
- return isFixedOrientationLandscape(orientation) || isFixedOrientationPortrait(orientation);
- }
-
- /**
* Returns true if the activity's orientation is fixed to landscape.
* @hide
*/
@@ -1157,6 +1155,7 @@ public class ActivityInfo extends ComponentInfo
dest.writeString(permission);
dest.writeString(taskAffinity);
dest.writeString(targetActivity);
+ dest.writeString(launchToken);
dest.writeInt(flags);
dest.writeInt(screenOrientation);
dest.writeInt(configChanges);
@@ -1189,6 +1188,7 @@ public class ActivityInfo extends ComponentInfo
* Determines whether the {@link Activity} is considered translucent or floating.
* @hide
*/
+ @TestApi
public static boolean isTranslucentOrFloating(TypedArray attributes) {
final boolean isTranslucent =
attributes.getBoolean(com.android.internal.R.styleable.Window_windowIsTranslucent,
@@ -1204,6 +1204,67 @@ public class ActivityInfo extends ComponentInfo
return isFloating || isTranslucent || isSwipeToDismiss;
}
+ /**
+ * Convert the screen orientation constant to a human readable format.
+ * @hide
+ */
+ public static String screenOrientationToString(int orientation) {
+ switch (orientation) {
+ case SCREEN_ORIENTATION_UNSET:
+ return "SCREEN_ORIENTATION_UNSET";
+ case SCREEN_ORIENTATION_UNSPECIFIED:
+ return "SCREEN_ORIENTATION_UNSPECIFIED";
+ case SCREEN_ORIENTATION_LANDSCAPE:
+ return "SCREEN_ORIENTATION_LANDSCAPE";
+ case SCREEN_ORIENTATION_PORTRAIT:
+ return "SCREEN_ORIENTATION_PORTRAIT";
+ case SCREEN_ORIENTATION_USER:
+ return "SCREEN_ORIENTATION_USER";
+ case SCREEN_ORIENTATION_BEHIND:
+ return "SCREEN_ORIENTATION_BEHIND";
+ case SCREEN_ORIENTATION_SENSOR:
+ return "SCREEN_ORIENTATION_SENSOR";
+ case SCREEN_ORIENTATION_NOSENSOR:
+ return "SCREEN_ORIENTATION_NOSENSOR";
+ case SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
+ return "SCREEN_ORIENTATION_SENSOR_LANDSCAPE";
+ case SCREEN_ORIENTATION_SENSOR_PORTRAIT:
+ return "SCREEN_ORIENTATION_SENSOR_PORTRAIT";
+ case SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
+ return "SCREEN_ORIENTATION_REVERSE_LANDSCAPE";
+ case SCREEN_ORIENTATION_REVERSE_PORTRAIT:
+ return "SCREEN_ORIENTATION_REVERSE_PORTRAIT";
+ case SCREEN_ORIENTATION_FULL_SENSOR:
+ return "SCREEN_ORIENTATION_FULL_SENSOR";
+ case SCREEN_ORIENTATION_USER_LANDSCAPE:
+ return "SCREEN_ORIENTATION_USER_LANDSCAPE";
+ case SCREEN_ORIENTATION_USER_PORTRAIT:
+ return "SCREEN_ORIENTATION_USER_PORTRAIT";
+ case SCREEN_ORIENTATION_FULL_USER:
+ return "SCREEN_ORIENTATION_FULL_USER";
+ case SCREEN_ORIENTATION_LOCKED:
+ return "SCREEN_ORIENTATION_LOCKED";
+ default:
+ return Integer.toString(orientation);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static String colorModeToString(@ColorMode int colorMode) {
+ switch (colorMode) {
+ case COLOR_MODE_DEFAULT:
+ return "COLOR_MODE_DEFAULT";
+ case COLOR_MODE_WIDE_COLOR_GAMUT:
+ return "COLOR_MODE_WIDE_COLOR_GAMUT";
+ case COLOR_MODE_HDR:
+ return "COLOR_MODE_HDR";
+ default:
+ return Integer.toString(colorMode);
+ }
+ }
+
public static final Parcelable.Creator<ActivityInfo> CREATOR
= new Parcelable.Creator<ActivityInfo>() {
public ActivityInfo createFromParcel(Parcel source) {
@@ -1222,6 +1283,7 @@ public class ActivityInfo extends ComponentInfo
permission = source.readString();
taskAffinity = source.readString();
targetActivity = source.readString();
+ launchToken = source.readString();
flags = source.readInt();
screenOrientation = source.readInt();
configChanges = source.readInt();
diff --git a/core/java/android/content/pm/AndroidTestBaseUpdater.java b/core/java/android/content/pm/AndroidTestBaseUpdater.java
new file mode 100644
index 000000000000..2aaac0280a0e
--- /dev/null
+++ b/core/java/android/content/pm/AndroidTestBaseUpdater.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+package android.content.pm;
+
+import static android.content.pm.SharedLibraryNames.ANDROID_TEST_BASE;
+import static android.content.pm.SharedLibraryNames.ANDROID_TEST_RUNNER;
+
+import android.content.pm.PackageParser.Package;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Updates a package to ensure that if it targets < P that the android.test.base library is
+ * included by default.
+ *
+ * <p>This is separated out so that it can be conditionally included at build time depending on
+ * whether android.test.base is on the bootclasspath or not. In order to include this at
+ * build time, and remove android.test.base from the bootclasspath pass
+ * REMOVE_ATB_FROM_BCP=true on the build command line, otherwise this class will not be included
+ * and the
+ *
+ * @hide
+ */
+@VisibleForTesting
+public class AndroidTestBaseUpdater extends PackageSharedLibraryUpdater {
+
+ @Override
+ public void updatePackage(Package pkg) {
+ // Packages targeted at <= O_MR1 expect the classes in the android.test.base library
+ // to be accessible so this maintains backward compatibility by adding the
+ // android.test.base library to those packages.
+ if (apkTargetsApiLevelLessThanOrEqualToOMR1(pkg)) {
+ prefixRequiredLibrary(pkg, ANDROID_TEST_BASE);
+ } else {
+ // If a package already depends on android.test.runner then add a dependency on
+ // android.test.base because android.test.runner depends on classes from the
+ // android.test.base library.
+ prefixImplicitDependency(pkg, ANDROID_TEST_RUNNER, ANDROID_TEST_BASE);
+ }
+ }
+}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index f04f7633c0ba..d65e051b70b4 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -19,12 +19,14 @@ package android.content.pm;
import static android.os.Build.VERSION_CODES.DONUT;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Environment;
import android.os.Parcel;
import android.os.Parcelable;
@@ -33,6 +35,7 @@ import android.os.storage.StorageManager;
import android.text.TextUtils;
import android.util.Printer;
import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.util.ArrayUtils;
import com.android.server.SystemConfig;
@@ -376,7 +379,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
* {@code DownloadManager}, {@code MediaPlayer}) will refuse app's requests to use cleartext
* traffic. Third-party libraries are encouraged to honor this flag as well.
*
- * <p>NOTE: {@code WebView} does not honor this flag.
+ * <p>NOTE: {@code WebView} honors this flag for applications targeting API level 26 and up.
*
* <p>This flag is ignored on Android N and above if an Android Network Security Config is
* present.
@@ -587,24 +590,56 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
*/
public static final int PRIVATE_FLAG_VIRTUAL_PRELOAD = 1 << 16;
+ /**
+ * Value for {@link #privateFlags}: whether this app is pre-installed on the
+ * OEM partition of the system image.
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_OEM = 1 << 17;
+
+ /**
+ * Value for {@link #privateFlags}: whether this app is pre-installed on the
+ * vendor partition of the system image.
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_VENDOR = 1 << 18;
+
+ /**
+ * Value for {@link #privateFlags}: whether this app is pre-installed on the
+ * product partition of the system image.
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_PRODUCT = 1 << 19;
+
+ /**
+ * Value for {@link #privateFlags}: whether this app is signed with the
+ * platform key.
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY = 1 << 20;
+
/** @hide */
@IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = {
- PRIVATE_FLAG_HIDDEN,
+ PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE,
+ PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION,
+ PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE,
+ PRIVATE_FLAG_BACKUP_IN_FOREGROUND,
PRIVATE_FLAG_CANT_SAVE_STATE,
- PRIVATE_FLAG_FORWARD_LOCK,
- PRIVATE_FLAG_PRIVILEGED,
- PRIVATE_FLAG_HAS_DOMAIN_URLS,
PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE,
PRIVATE_FLAG_DIRECT_BOOT_AWARE,
+ PRIVATE_FLAG_FORWARD_LOCK,
+ PRIVATE_FLAG_HAS_DOMAIN_URLS,
+ PRIVATE_FLAG_HIDDEN,
PRIVATE_FLAG_INSTANT,
+ PRIVATE_FLAG_ISOLATED_SPLIT_LOADING,
+ PRIVATE_FLAG_OEM,
PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE,
+ PRIVATE_FLAG_PRIVILEGED,
+ PRIVATE_FLAG_PRODUCT,
PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER,
- PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE,
- PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE,
- PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION,
- PRIVATE_FLAG_BACKUP_IN_FOREGROUND,
+ PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY,
PRIVATE_FLAG_STATIC_SHARED_LIBRARY,
- PRIVATE_FLAG_ISOLATED_SPLIT_LOADING,
+ PRIVATE_FLAG_VENDOR,
PRIVATE_FLAG_VIRTUAL_PRELOAD,
})
@Retention(RetentionPolicy.SOURCE)
@@ -737,15 +772,13 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
public String[] resourceDirs;
/**
- * String retrieved from the seinfo tag found in selinux policy. This value
- * can be overridden with a value set through the mac_permissions.xml policy
- * construct. This value is useful in setting an SELinux security context on
- * the process as well as its data directory. The String default is being used
- * here to represent a catchall label when no policy matches.
+ * String retrieved from the seinfo tag found in selinux policy. This value can be set through
+ * the mac_permissions.xml policy construct. This value is used for setting an SELinux security
+ * context on the process as well as its data directory.
*
* {@hide}
*/
- public String seInfo = "default";
+ public String seInfo;
/**
* The seinfo tag generated per-user. This value may change based upon the
@@ -880,9 +913,42 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
* The app's declared version code.
* @hide
*/
+ public long longVersionCode;
+
+ /**
+ * An integer representation of the app's declared version code. This is being left in place as
+ * some apps were using reflection to access it before the move to long in
+ * {@link android.os.Build.VERSION_CODES#P}
+ * @deprecated Use {@link #longVersionCode} instead.
+ * @hide
+ */
+ @Deprecated
public int versionCode;
/**
+ * The user-visible SDK version (ex. 26) of the framework against which the application claims
+ * to have been compiled, or {@code 0} if not specified.
+ * <p>
+ * This property is the compile-time equivalent of
+ * {@link android.os.Build.VERSION#CODENAME Build.VERSION.SDK_INT}.
+ *
+ * @hide For platform use only; we don't expect developers to need to read this value.
+ */
+ public int compileSdkVersion;
+
+ /**
+ * The development codename (ex. "O", "REL") of the framework against which the application
+ * claims to have been compiled, or {@code null} if not specified.
+ * <p>
+ * This property is the compile-time equivalent of
+ * {@link android.os.Build.VERSION#CODENAME Build.VERSION.CODENAME}.
+ *
+ * @hide For platform use only; we don't expect developers to need to read this value.
+ */
+ @Nullable
+ public String compileSdkVersionCodename;
+
+ /**
* When false, indicates that all components within this application are
* considered disabled, regardless of their individually set enabled status.
*/
@@ -910,9 +976,17 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
* Version of the sandbox the application wants to run in.
* @hide
*/
+ @SystemApi
public int targetSandboxVersion;
/**
+ * The factory of this package, as specified by the &lt;manifest&gt;
+ * tag's {@link android.R.styleable#AndroidManifestApplication_appComponentFactory}
+ * attribute.
+ */
+ public String appComponentFactory;
+
+ /**
* The category of this app. Categories are used to cluster multiple apps
* together into meaningful groups, such as when summarizing battery,
* network, or disk usage. Apps should only define this value when they fit
@@ -1093,7 +1167,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
@Retention(RetentionPolicy.SOURCE)
public @interface HiddenApiEnforcementPolicy {}
- private boolean isValidHiddenApiEnforcementPolicy(int policy) {
+ /** @hide */
+ public static boolean isValidHiddenApiEnforcementPolicy(int policy) {
return policy >= HIDDEN_API_ENFORCEMENT_DEFAULT && policy <= HIDDEN_API_ENFORCEMENT_MAX;
}
@@ -1160,7 +1235,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
pw.println(prefix + "enabled=" + enabled
+ " minSdkVersion=" + minSdkVersion
+ " targetSdkVersion=" + targetSdkVersion
- + " versionCode=" + versionCode
+ + " versionCode=" + longVersionCode
+ " targetSandboxVersion=" + targetSandboxVersion);
if ((dumpFlags & DUMP_FLAG_DETAILS) != 0) {
if (manageSpaceActivityName != null) {
@@ -1191,6 +1266,105 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
super.dumpBack(pw, prefix);
}
+ /** {@hide} */
+ public void writeToProto(ProtoOutputStream proto, long fieldId, int dumpFlags) {
+ long token = proto.start(fieldId);
+ super.writeToProto(proto, ApplicationInfoProto.PACKAGE);
+ proto.write(ApplicationInfoProto.PERMISSION, permission);
+ proto.write(ApplicationInfoProto.PROCESS_NAME, processName);
+ proto.write(ApplicationInfoProto.UID, uid);
+ proto.write(ApplicationInfoProto.FLAGS, flags);
+ proto.write(ApplicationInfoProto.PRIVATE_FLAGS, privateFlags);
+ proto.write(ApplicationInfoProto.THEME, theme);
+ proto.write(ApplicationInfoProto.SOURCE_DIR, sourceDir);
+ if (!Objects.equals(sourceDir, publicSourceDir)) {
+ proto.write(ApplicationInfoProto.PUBLIC_SOURCE_DIR, publicSourceDir);
+ }
+ if (!ArrayUtils.isEmpty(splitSourceDirs)) {
+ for (String dir : splitSourceDirs) {
+ proto.write(ApplicationInfoProto.SPLIT_SOURCE_DIRS, dir);
+ }
+ }
+ if (!ArrayUtils.isEmpty(splitPublicSourceDirs)
+ && !Arrays.equals(splitSourceDirs, splitPublicSourceDirs)) {
+ for (String dir : splitPublicSourceDirs) {
+ proto.write(ApplicationInfoProto.SPLIT_PUBLIC_SOURCE_DIRS, dir);
+ }
+ }
+ if (resourceDirs != null) {
+ for (String dir : resourceDirs) {
+ proto.write(ApplicationInfoProto.RESOURCE_DIRS, dir);
+ }
+ }
+ proto.write(ApplicationInfoProto.DATA_DIR, dataDir);
+ proto.write(ApplicationInfoProto.CLASS_LOADER_NAME, classLoaderName);
+ if (!ArrayUtils.isEmpty(splitClassLoaderNames)) {
+ for (String name : splitClassLoaderNames) {
+ proto.write(ApplicationInfoProto.SPLIT_CLASS_LOADER_NAMES, name);
+ }
+ }
+
+ long versionToken = proto.start(ApplicationInfoProto.VERSION);
+ proto.write(ApplicationInfoProto.Version.ENABLED, enabled);
+ proto.write(ApplicationInfoProto.Version.MIN_SDK_VERSION, minSdkVersion);
+ proto.write(ApplicationInfoProto.Version.TARGET_SDK_VERSION, targetSdkVersion);
+ proto.write(ApplicationInfoProto.Version.VERSION_CODE, longVersionCode);
+ proto.write(ApplicationInfoProto.Version.TARGET_SANDBOX_VERSION, targetSandboxVersion);
+ proto.end(versionToken);
+
+ if ((dumpFlags & DUMP_FLAG_DETAILS) != 0) {
+ long detailToken = proto.start(ApplicationInfoProto.DETAIL);
+ if (className != null) {
+ proto.write(ApplicationInfoProto.Detail.CLASS_NAME, className);
+ }
+ proto.write(ApplicationInfoProto.Detail.TASK_AFFINITY, taskAffinity);
+ proto.write(ApplicationInfoProto.Detail.REQUIRES_SMALLEST_WIDTH_DP,
+ requiresSmallestWidthDp);
+ proto.write(ApplicationInfoProto.Detail.COMPATIBLE_WIDTH_LIMIT_DP,
+ compatibleWidthLimitDp);
+ proto.write(ApplicationInfoProto.Detail.LARGEST_WIDTH_LIMIT_DP,
+ largestWidthLimitDp);
+ if (seInfo != null) {
+ proto.write(ApplicationInfoProto.Detail.SEINFO, seInfo);
+ proto.write(ApplicationInfoProto.Detail.SEINFO_USER, seInfoUser);
+ }
+ proto.write(ApplicationInfoProto.Detail.DEVICE_PROTECTED_DATA_DIR,
+ deviceProtectedDataDir);
+ proto.write(ApplicationInfoProto.Detail.CREDENTIAL_PROTECTED_DATA_DIR,
+ credentialProtectedDataDir);
+ if (sharedLibraryFiles != null) {
+ for (String f : sharedLibraryFiles) {
+ proto.write(ApplicationInfoProto.Detail.SHARED_LIBRARY_FILES, f);
+ }
+ }
+ if (manageSpaceActivityName != null) {
+ proto.write(ApplicationInfoProto.Detail.MANAGE_SPACE_ACTIVITY_NAME,
+ manageSpaceActivityName);
+ }
+ if (descriptionRes != 0) {
+ proto.write(ApplicationInfoProto.Detail.DESCRIPTION_RES, descriptionRes);
+ }
+ if (uiOptions != 0) {
+ proto.write(ApplicationInfoProto.Detail.UI_OPTIONS, uiOptions);
+ }
+ proto.write(ApplicationInfoProto.Detail.SUPPORTS_RTL, hasRtlSupport());
+ if (fullBackupContent > 0) {
+ proto.write(ApplicationInfoProto.Detail.CONTENT, "@xml/" + fullBackupContent);
+ } else {
+ proto.write(ApplicationInfoProto.Detail.IS_FULL_BACKUP, fullBackupContent == 0);
+ }
+ if (networkSecurityConfigRes != 0) {
+ proto.write(ApplicationInfoProto.Detail.NETWORK_SECURITY_CONFIG_RES,
+ networkSecurityConfigRes);
+ }
+ if (category != CATEGORY_UNDEFINED) {
+ proto.write(ApplicationInfoProto.Detail.CATEGORY, category);
+ }
+ proto.end(detailToken);
+ }
+ proto.end(token);
+ }
+
/**
* @return true if "supportsRtl" has been set to true in the AndroidManifest
* @hide
@@ -1268,7 +1442,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
uid = orig.uid;
minSdkVersion = orig.minSdkVersion;
targetSdkVersion = orig.targetSdkVersion;
- versionCode = orig.versionCode;
+ setVersionCode(orig.longVersionCode);
enabled = orig.enabled;
enabledSetting = orig.enabledSetting;
installLocation = orig.installLocation;
@@ -1282,6 +1456,9 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
targetSandboxVersion = orig.targetSandboxVersion;
classLoaderName = orig.classLoaderName;
splitClassLoaderNames = orig.splitClassLoaderNames;
+ appComponentFactory = orig.appComponentFactory;
+ compileSdkVersion = orig.compileSdkVersion;
+ compileSdkVersionCodename = orig.compileSdkVersionCodename;
mHiddenApiPolicy = orig.mHiddenApiPolicy;
}
@@ -1339,7 +1516,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
dest.writeInt(uid);
dest.writeInt(minSdkVersion);
dest.writeInt(targetSdkVersion);
- dest.writeInt(versionCode);
+ dest.writeLong(longVersionCode);
dest.writeInt(enabled ? 1 : 0);
dest.writeInt(enabledSetting);
dest.writeInt(installLocation);
@@ -1353,6 +1530,9 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
dest.writeInt(targetSandboxVersion);
dest.writeString(classLoaderName);
dest.writeStringArray(splitClassLoaderNames);
+ dest.writeInt(compileSdkVersion);
+ dest.writeString(compileSdkVersionCodename);
+ dest.writeString(appComponentFactory);
dest.writeInt(mHiddenApiPolicy);
}
@@ -1407,7 +1587,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
uid = source.readInt();
minSdkVersion = source.readInt();
targetSdkVersion = source.readInt();
- versionCode = source.readInt();
+ setVersionCode(source.readLong());
enabled = source.readInt() != 0;
enabledSetting = source.readInt();
installLocation = source.readInt();
@@ -1421,6 +1601,9 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
targetSandboxVersion = source.readInt();
classLoaderName = source.readString();
splitClassLoaderNames = source.readStringArray();
+ compileSdkVersion = source.readInt();
+ compileSdkVersionCodename = source.readString();
+ appComponentFactory = source.readString();
mHiddenApiPolicy = source.readInt();
}
@@ -1492,42 +1675,30 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
}
}
- /**
- * @hide
- */
- @Override
- public Drawable loadDefaultIcon(PackageManager pm) {
- if ((flags & FLAG_EXTERNAL_STORAGE) != 0
- && isPackageUnavailable(pm)) {
- return Resources.getSystem().getDrawable(
- com.android.internal.R.drawable.sym_app_on_sd_unavailable_icon);
- }
- return pm.getDefaultActivityIcon();
- }
-
- private boolean isPackageUnavailable(PackageManager pm) {
- try {
- return pm.getPackageInfo(packageName, 0) == null;
- } catch (NameNotFoundException ex) {
- return true;
- }
- }
-
private boolean isPackageWhitelistedForHiddenApis() {
return SystemConfig.getInstance().getHiddenApiWhitelistedApps().contains(packageName);
}
+ private boolean isAllowedToUseHiddenApis() {
+ return isSignedWithPlatformKey()
+ || (isPackageWhitelistedForHiddenApis() && (isSystemApp() || isUpdatedSystemApp()));
+ }
+
/**
* @hide
*/
public @HiddenApiEnforcementPolicy int getHiddenApiEnforcementPolicy() {
+ if (isAllowedToUseHiddenApis()) {
+ return HIDDEN_API_ENFORCEMENT_NONE;
+ }
if (mHiddenApiPolicy != HIDDEN_API_ENFORCEMENT_DEFAULT) {
return mHiddenApiPolicy;
}
- if (isPackageWhitelistedForHiddenApis() && (isSystemApp() || isUpdatedSystemApp())) {
- return HIDDEN_API_ENFORCEMENT_NONE;
+ if (targetSdkVersion < Build.VERSION_CODES.P) {
+ return HIDDEN_API_ENFORCEMENT_BLACK;
+ } else {
+ return HIDDEN_API_ENFORCEMENT_DARK_GREY_AND_BLACK;
}
- return HIDDEN_API_ENFORCEMENT_BLACK;
}
/**
@@ -1541,44 +1712,57 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
}
/**
+ * Updates the hidden API enforcement policy for this app from the given values, if appropriate.
+ *
+ * This will have no effect if this app is not subject to hidden API enforcement, i.e. if it
+ * is on the package whitelist.
+ *
+ * @param policyPreP configured policy for pre-P apps, or {@link
+ * #HIDDEN_API_ENFORCEMENT_DEFAULT} if nothing configured.
+ * @param policyP configured policy for apps targeting P or later, or {@link
+ * #HIDDEN_API_ENFORCEMENT_DEFAULT} if nothing configured.
* @hide
*/
- public boolean isForwardLocked() {
- return (privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
- }
+ public void maybeUpdateHiddenApiEnforcementPolicy(
+ @HiddenApiEnforcementPolicy int policyPreP, @HiddenApiEnforcementPolicy int policyP) {
+ if (isPackageWhitelistedForHiddenApis()) {
+ return;
+ }
+ if (targetSdkVersion < Build.VERSION_CODES.P) {
+ setHiddenApiEnforcementPolicy(policyPreP);
+ } else if (targetSdkVersion >= Build.VERSION_CODES.P) {
+ setHiddenApiEnforcementPolicy(policyP);
+ }
- /**
- * @hide
- */
- @TestApi
- public boolean isSystemApp() {
- return (flags & ApplicationInfo.FLAG_SYSTEM) != 0;
}
/**
* @hide
*/
- @TestApi
- public boolean isPrivilegedApp() {
- return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
+ public void setVersionCode(long newVersionCode) {
+ longVersionCode = newVersionCode;
+ versionCode = (int) newVersionCode;
}
/**
* @hide
*/
- public boolean isUpdatedSystemApp() {
- return (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
- }
-
- /** @hide */
- public boolean isInternal() {
- return (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == 0;
+ @Override
+ public Drawable loadDefaultIcon(PackageManager pm) {
+ if ((flags & FLAG_EXTERNAL_STORAGE) != 0
+ && isPackageUnavailable(pm)) {
+ return Resources.getSystem().getDrawable(
+ com.android.internal.R.drawable.sym_app_on_sd_unavailable_icon);
+ }
+ return pm.getDefaultActivityIcon();
}
-
- /** @hide */
- public boolean isExternalAsec() {
- return TextUtils.isEmpty(volumeUuid)
- && (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
+
+ private boolean isPackageUnavailable(PackageManager pm) {
+ try {
+ return pm.getPackageInfo(packageName, 0) == null;
+ } catch (NameNotFoundException ex) {
+ return true;
+ }
}
/** @hide */
@@ -1593,45 +1777,91 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
}
/** @hide */
- public boolean isPartiallyDirectBootAware() {
- return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE) != 0;
+ public boolean isEncryptionAware() {
+ return isDirectBootAware() || isPartiallyDirectBootAware();
}
/** @hide */
- public boolean isEncryptionAware() {
- return isDirectBootAware() || isPartiallyDirectBootAware();
+ public boolean isExternal() {
+ return (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
+ }
+
+ /** @hide */
+ public boolean isExternalAsec() {
+ return TextUtils.isEmpty(volumeUuid) && isExternal();
+ }
+
+ /** @hide */
+ public boolean isForwardLocked() {
+ return (privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
}
/**
+ * True if the application is installed as an instant app.
* @hide
*/
+ @SystemApi
public boolean isInstantApp() {
return (privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
}
- /**
- * @hide
- */
- public boolean isRequiredForSystemUser() {
- return (privateFlags & ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER) != 0;
+ /** @hide */
+ public boolean isInternal() {
+ return (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == 0;
}
- /**
- * Returns true if the app has declared in its manifest that it wants its split APKs to be
- * loaded into isolated Contexts, with their own ClassLoaders and Resources objects.
- * @hide
- */
- public boolean requestsIsolatedSplitLoading() {
- return (privateFlags & ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING) != 0;
+ /** @hide */
+ public boolean isOem() {
+ return (privateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0;
}
- /**
- * @hide
- */
+ /** @hide */
+ public boolean isPartiallyDirectBootAware() {
+ return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE) != 0;
+ }
+
+ /** @hide */
+ public boolean isSignedWithPlatformKey() {
+ return (privateFlags & ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY) != 0;
+ }
+
+ /** @hide */
+ @TestApi
+ public boolean isPrivilegedApp() {
+ return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
+ }
+
+ /** @hide */
+ public boolean isRequiredForSystemUser() {
+ return (privateFlags & ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER) != 0;
+ }
+
+ /** @hide */
public boolean isStaticSharedLibrary() {
return (privateFlags & ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY) != 0;
}
+ /** @hide */
+ @TestApi
+ public boolean isSystemApp() {
+ return (flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+ }
+
+ /** @hide */
+ public boolean isUpdatedSystemApp() {
+ return (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
+ }
+
+ /** @hide */
+ public boolean isVendor() {
+ return (privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
+ }
+
+ /** @hide */
+ public boolean isProduct() {
+ return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0;
+ }
+
/**
* Returns whether or not this application was installed as a virtual preload.
*/
@@ -1640,6 +1870,15 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
}
/**
+ * Returns true if the app has declared in its manifest that it wants its split APKs to be
+ * loaded into isolated Contexts, with their own ClassLoaders and Resources objects.
+ * @hide
+ */
+ public boolean requestsIsolatedSplitLoading() {
+ return (privateFlags & ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING) != 0;
+ }
+
+ /**
* @hide
*/
@Override protected ApplicationInfo getApplicationInfo() {
diff --git a/core/java/android/content/pm/AuxiliaryResolveInfo.java b/core/java/android/content/pm/AuxiliaryResolveInfo.java
index 067363d43adf..202df50dda6f 100644
--- a/core/java/android/content/pm/AuxiliaryResolveInfo.java
+++ b/core/java/android/content/pm/AuxiliaryResolveInfo.java
@@ -21,6 +21,10 @@ import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
+import android.os.Bundle;
+
+import java.util.Collections;
+import java.util.List;
/**
* Auxiliary application resolution response.
@@ -31,56 +35,95 @@ import android.content.IntentFilter;
* hasn't been installed.
* @hide
*/
-public final class AuxiliaryResolveInfo extends IntentFilter {
- /** Resolved information returned from the external instant resolver */
- public final InstantAppResolveInfo resolveInfo;
- /** The resolved package. Copied from {@link #resolveInfo}. */
- public final String packageName;
+public final class AuxiliaryResolveInfo {
/** The activity to launch if there's an installation failure. */
public final ComponentName installFailureActivity;
- /** The resolve split. Copied from the matched filter in {@link #resolveInfo}. */
- public final String splitName;
/** Whether or not instant resolution needs the second phase */
public final boolean needsPhaseTwo;
/** Opaque token to track the instant application resolution */
public final String token;
- /** The version code of the package */
- public final int versionCode;
/** An intent to start upon failure to install */
public final Intent failureIntent;
+ /** The matching filters for this resolve info. */
+ public final List<AuxiliaryFilter> filters;
/** Create a response for installing an instant application. */
- public AuxiliaryResolveInfo(@NonNull InstantAppResolveInfo resolveInfo,
- @NonNull IntentFilter orig,
- @Nullable String splitName,
- @NonNull String token,
+ public AuxiliaryResolveInfo(@NonNull String token,
boolean needsPhase2,
- @Nullable Intent failureIntent) {
- super(orig);
- this.resolveInfo = resolveInfo;
- this.packageName = resolveInfo.getPackageName();
- this.splitName = splitName;
+ @Nullable Intent failureIntent,
+ @Nullable List<AuxiliaryFilter> filters) {
this.token = token;
this.needsPhaseTwo = needsPhase2;
- this.versionCode = resolveInfo.getVersionCode();
this.failureIntent = failureIntent;
+ this.filters = filters;
this.installFailureActivity = null;
}
/** Create a response for installing a split on demand. */
- public AuxiliaryResolveInfo(@NonNull String packageName,
- @Nullable String splitName,
- @Nullable ComponentName failureActivity,
- int versionCode,
- @Nullable Intent failureIntent) {
+ public AuxiliaryResolveInfo(@Nullable ComponentName failureActivity,
+ @Nullable Intent failureIntent,
+ @Nullable List<AuxiliaryFilter> filters) {
super();
- this.packageName = packageName;
this.installFailureActivity = failureActivity;
- this.splitName = splitName;
- this.versionCode = versionCode;
- this.resolveInfo = null;
+ this.filters = filters;
this.token = null;
this.needsPhaseTwo = false;
this.failureIntent = failureIntent;
}
+
+ /** Create a response for installing a split on demand. */
+ public AuxiliaryResolveInfo(@Nullable ComponentName failureActivity,
+ String packageName, long versionCode, String splitName) {
+ this(failureActivity, null, Collections.singletonList(
+ new AuxiliaryResolveInfo.AuxiliaryFilter(packageName, versionCode, splitName)));
+ }
+
+ /** @hide */
+ public static final class AuxiliaryFilter extends IntentFilter {
+ /** Resolved information returned from the external instant resolver */
+ public final InstantAppResolveInfo resolveInfo;
+ /** The resolved package. Copied from {@link #resolveInfo}. */
+ public final String packageName;
+ /** The version code of the package */
+ public final long versionCode;
+ /** The resolve split. Copied from the matched filter in {@link #resolveInfo}. */
+ public final String splitName;
+ /** The extras to pass on to the installer for this filter. */
+ public final Bundle extras;
+
+ public AuxiliaryFilter(IntentFilter orig, InstantAppResolveInfo resolveInfo,
+ String splitName, Bundle extras) {
+ super(orig);
+ this.resolveInfo = resolveInfo;
+ this.packageName = resolveInfo.getPackageName();
+ this.versionCode = resolveInfo.getLongVersionCode();
+ this.splitName = splitName;
+ this.extras = extras;
+ }
+
+ public AuxiliaryFilter(InstantAppResolveInfo resolveInfo,
+ String splitName, Bundle extras) {
+ this.resolveInfo = resolveInfo;
+ this.packageName = resolveInfo.getPackageName();
+ this.versionCode = resolveInfo.getLongVersionCode();
+ this.splitName = splitName;
+ this.extras = extras;
+ }
+
+ public AuxiliaryFilter(String packageName, long versionCode, String splitName) {
+ this.resolveInfo = null;
+ this.packageName = packageName;
+ this.versionCode = versionCode;
+ this.splitName = splitName;
+ this.extras = null;
+ }
+
+ @Override
+ public String toString() {
+ return "AuxiliaryFilter{"
+ + "packageName='" + packageName + '\''
+ + ", versionCode=" + versionCode
+ + ", splitName='" + splitName + '\'' + '}';
+ }
+ }
} \ No newline at end of file
diff --git a/core/java/android/content/pm/ComponentInfo.java b/core/java/android/content/pm/ComponentInfo.java
index 6b1222f53959..0269b6c3e5e1 100644
--- a/core/java/android/content/pm/ComponentInfo.java
+++ b/core/java/android/content/pm/ComponentInfo.java
@@ -96,7 +96,8 @@ public class ComponentInfo extends PackageItemInfo {
encryptionAware = directBootAware = orig.directBootAware;
}
- @Override public CharSequence loadLabel(PackageManager pm) {
+ /** @hide */
+ @Override public CharSequence loadUnsafeLabel(PackageManager pm) {
if (nonLocalizedLabel != null) {
return nonLocalizedLabel;
}
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
new file mode 100644
index 000000000000..87f4dab1115a
--- /dev/null
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.pm;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import com.android.internal.R;
+import com.android.internal.util.UserIcons;
+
+import java.util.List;
+
+/**
+ * Class for handling cross profile operations. Apps can use this class to interact with its
+ * instance in any profile that is in {@link #getTargetUserProfiles()}. For example, app can
+ * use this class to start its main activity in managed profile.
+ */
+public class CrossProfileApps {
+ private final Context mContext;
+ private final ICrossProfileApps mService;
+ private final UserManager mUserManager;
+ private final Resources mResources;
+
+ /** @hide */
+ public CrossProfileApps(Context context, ICrossProfileApps service) {
+ mContext = context;
+ mService = service;
+ mUserManager = context.getSystemService(UserManager.class);
+ mResources = context.getResources();
+ }
+
+ /**
+ * Starts the specified main activity of the caller package in the specified profile.
+ *
+ * @param component The ComponentName of the activity to launch, it must be exported and has
+ * action {@link android.content.Intent#ACTION_MAIN}, category
+ * {@link android.content.Intent#CATEGORY_LAUNCHER}. Otherwise, SecurityException will
+ * be thrown.
+ * @param targetUser The UserHandle of the profile, must be one of the users returned by
+ * {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
+ * be thrown.
+ */
+ public void startMainActivity(@NonNull ComponentName component,
+ @NonNull UserHandle targetUser) {
+ try {
+ mService.startActivityAsUser(
+ mContext.getIApplicationThread(),
+ mContext.getPackageName(),
+ component,
+ targetUser);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Return a list of user profiles that that the caller can use when calling other APIs in this
+ * class.
+ * <p>
+ * A user profile would be considered as a valid target user profile, provided that:
+ * <ul>
+ * <li>It gets caller app installed</li>
+ * <li>It is not equal to the calling user</li>
+ * <li>It is in the same profile group of calling user profile</li>
+ * <li>It is enabled</li>
+ * </ul>
+ *
+ * @see UserManager#getUserProfiles()
+ */
+ public @NonNull List<UserHandle> getTargetUserProfiles() {
+ try {
+ return mService.getTargetUserProfiles(mContext.getPackageName());
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Return a label that calling app can show to user for the semantic of profile switching --
+ * launching its own activity in specified user profile. For example, it may return
+ * "Switch to work" if the given user handle is the managed profile one.
+ *
+ * @param userHandle The UserHandle of the target profile, must be one of the users returned by
+ * {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
+ * be thrown.
+ * @return a label that calling app can show user for the semantic of launching its own
+ * activity in the specified user profile.
+ *
+ * @see #startMainActivity(ComponentName, UserHandle, Rect, Bundle)
+ */
+ public @NonNull CharSequence getProfileSwitchingLabel(@NonNull UserHandle userHandle) {
+ verifyCanAccessUser(userHandle);
+
+ final int stringRes = mUserManager.isManagedProfile(userHandle.getIdentifier())
+ ? R.string.managed_profile_label
+ : R.string.user_owner_label;
+ return mResources.getString(stringRes);
+ }
+
+ /**
+ * Return a drawable that calling app can show to user for the semantic of profile switching --
+ * launching its own activity in specified user profile. For example, it may return a briefcase
+ * icon if the given user handle is the managed profile one.
+ *
+ * @param userHandle The UserHandle of the target profile, must be one of the users returned by
+ * {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
+ * be thrown.
+ * @return an icon that calling app can show user for the semantic of launching its own
+ * activity in specified user profile.
+ *
+ * @see #startMainActivity(ComponentName, UserHandle)
+ */
+ public @NonNull Drawable getProfileSwitchingIconDrawable(@NonNull UserHandle userHandle) {
+ verifyCanAccessUser(userHandle);
+
+ final boolean isManagedProfile =
+ mUserManager.isManagedProfile(userHandle.getIdentifier());
+ if (isManagedProfile) {
+ return mResources.getDrawable(R.drawable.ic_corp_badge, null);
+ } else {
+ return UserIcons.getDefaultUserIcon(
+ mResources, UserHandle.USER_SYSTEM, true /* light */);
+ }
+ }
+
+ private void verifyCanAccessUser(UserHandle userHandle) {
+ if (!getTargetUserProfiles().contains(userHandle)) {
+ throw new SecurityException("Not allowed to access " + userHandle);
+ }
+ }
+}
diff --git a/core/java/android/content/pm/EphemeralIntentFilter.java b/core/java/android/content/pm/EphemeralIntentFilter.java
deleted file mode 100644
index 1dbbf816ed93..000000000000
--- a/core/java/android/content/pm/EphemeralIntentFilter.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.content.IntentFilter;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Information about an ephemeral application intent filter.
- * @hide
- * @removed
- */
-@Deprecated
-@SystemApi
-public final class EphemeralIntentFilter implements Parcelable {
- private final InstantAppIntentFilter mInstantAppIntentFilter;
-
- public EphemeralIntentFilter(@Nullable String splitName, @NonNull List<IntentFilter> filters) {
- mInstantAppIntentFilter = new InstantAppIntentFilter(splitName, filters);
- }
-
- EphemeralIntentFilter(@NonNull InstantAppIntentFilter intentFilter) {
- mInstantAppIntentFilter = intentFilter;
- }
-
- EphemeralIntentFilter(Parcel in) {
- mInstantAppIntentFilter = in.readParcelable(null /*loader*/);
- }
-
- public String getSplitName() {
- return mInstantAppIntentFilter.getSplitName();
- }
-
- public List<IntentFilter> getFilters() {
- return mInstantAppIntentFilter.getFilters();
- }
-
- /** @hide */
- InstantAppIntentFilter getInstantAppIntentFilter() {
- return mInstantAppIntentFilter;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeParcelable(mInstantAppIntentFilter, flags);
- }
-
- public static final Parcelable.Creator<EphemeralIntentFilter> CREATOR
- = new Parcelable.Creator<EphemeralIntentFilter>() {
- @Override
- public EphemeralIntentFilter createFromParcel(Parcel in) {
- return new EphemeralIntentFilter(in);
- }
- @Override
- public EphemeralIntentFilter[] newArray(int size) {
- return new EphemeralIntentFilter[size];
- }
- };
-}
diff --git a/core/java/android/content/pm/EphemeralResolveInfo.java b/core/java/android/content/pm/EphemeralResolveInfo.java
deleted file mode 100644
index 12131a3ebc98..000000000000
--- a/core/java/android/content/pm/EphemeralResolveInfo.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.content.IntentFilter;
-import android.content.pm.InstantAppResolveInfo.InstantAppDigest;
-import android.net.Uri;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-
-/**
- * Information about an ephemeral application.
- * @hide
- * @removed
- */
-@Deprecated
-@SystemApi
-public final class EphemeralResolveInfo implements Parcelable {
- /** Algorithm that will be used to generate the domain digest */
- public static final String SHA_ALGORITHM = "SHA-256";
-
- private final InstantAppResolveInfo mInstantAppResolveInfo;
- @Deprecated
- private final List<IntentFilter> mLegacyFilters;
-
- @Deprecated
- public EphemeralResolveInfo(@NonNull Uri uri, @NonNull String packageName,
- @NonNull List<IntentFilter> filters) {
- if (uri == null || packageName == null || filters == null || filters.isEmpty()) {
- throw new IllegalArgumentException();
- }
- final List<EphemeralIntentFilter> ephemeralFilters = new ArrayList<>(1);
- ephemeralFilters.add(new EphemeralIntentFilter(packageName, filters));
- mInstantAppResolveInfo = new InstantAppResolveInfo(uri.getHost(), packageName,
- createInstantAppIntentFilterList(ephemeralFilters));
- mLegacyFilters = new ArrayList<IntentFilter>(filters.size());
- mLegacyFilters.addAll(filters);
- }
-
- @Deprecated
- public EphemeralResolveInfo(@NonNull EphemeralDigest digest, @Nullable String packageName,
- @Nullable List<EphemeralIntentFilter> filters) {
- this(digest, packageName, filters, -1 /*versionCode*/);
- }
-
- public EphemeralResolveInfo(@NonNull EphemeralDigest digest, @Nullable String packageName,
- @Nullable List<EphemeralIntentFilter> filters, int versionCode) {
- mInstantAppResolveInfo = new InstantAppResolveInfo(
- digest.getInstantAppDigest(), packageName,
- createInstantAppIntentFilterList(filters), versionCode);
- mLegacyFilters = null;
- }
-
- public EphemeralResolveInfo(@NonNull String hostName, @Nullable String packageName,
- @Nullable List<EphemeralIntentFilter> filters) {
- this(new EphemeralDigest(hostName), packageName, filters);
- }
-
- EphemeralResolveInfo(Parcel in) {
- mInstantAppResolveInfo = in.readParcelable(null /*loader*/);
- mLegacyFilters = new ArrayList<IntentFilter>();
- in.readList(mLegacyFilters, null /*loader*/);
- }
-
- /** @hide */
- public InstantAppResolveInfo getInstantAppResolveInfo() {
- return mInstantAppResolveInfo;
- }
-
- private static List<InstantAppIntentFilter> createInstantAppIntentFilterList(
- List<EphemeralIntentFilter> filters) {
- if (filters == null) {
- return null;
- }
- final int filterCount = filters.size();
- final List<InstantAppIntentFilter> returnList = new ArrayList<>(filterCount);
- for (int i = 0; i < filterCount; i++) {
- returnList.add(filters.get(i).getInstantAppIntentFilter());
- }
- return returnList;
- }
-
- public byte[] getDigestBytes() {
- return mInstantAppResolveInfo.getDigestBytes();
- }
-
- public int getDigestPrefix() {
- return mInstantAppResolveInfo.getDigestPrefix();
- }
-
- public String getPackageName() {
- return mInstantAppResolveInfo.getPackageName();
- }
-
- public List<EphemeralIntentFilter> getIntentFilters() {
- final List<InstantAppIntentFilter> filters = mInstantAppResolveInfo.getIntentFilters();
- final int filterCount = filters.size();
- final List<EphemeralIntentFilter> returnList = new ArrayList<>(filterCount);
- for (int i = 0; i < filterCount; i++) {
- returnList.add(new EphemeralIntentFilter(filters.get(i)));
- }
- return returnList;
- }
-
- public int getVersionCode() {
- return mInstantAppResolveInfo.getVersionCode();
- }
-
- @Deprecated
- public List<IntentFilter> getFilters() {
- return mLegacyFilters;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeParcelable(mInstantAppResolveInfo, flags);
- out.writeList(mLegacyFilters);
- }
-
- public static final Parcelable.Creator<EphemeralResolveInfo> CREATOR
- = new Parcelable.Creator<EphemeralResolveInfo>() {
- @Override
- public EphemeralResolveInfo createFromParcel(Parcel in) {
- return new EphemeralResolveInfo(in);
- }
- @Override
- public EphemeralResolveInfo[] newArray(int size) {
- return new EphemeralResolveInfo[size];
- }
- };
-
- /**
- * Helper class to generate and store each of the digests and prefixes
- * sent to the Ephemeral Resolver.
- * <p>
- * Since intent filters may want to handle multiple hosts within a
- * domain [eg “*.google.com”], the resolver is presented with multiple
- * hash prefixes. For example, "a.b.c.d.e" generates digests for
- * "d.e", "c.d.e", "b.c.d.e" and "a.b.c.d.e".
- *
- * @hide
- */
- @SystemApi
- public static final class EphemeralDigest implements Parcelable {
- private final InstantAppDigest mInstantAppDigest;
-
- public EphemeralDigest(@NonNull String hostName) {
- this(hostName, -1 /*maxDigests*/);
- }
-
- /** @hide */
- public EphemeralDigest(@NonNull String hostName, int maxDigests) {
- mInstantAppDigest = new InstantAppDigest(hostName, maxDigests);
- }
-
- EphemeralDigest(Parcel in) {
- mInstantAppDigest = in.readParcelable(null /*loader*/);
- }
-
- /** @hide */
- InstantAppDigest getInstantAppDigest() {
- return mInstantAppDigest;
- }
-
- public byte[][] getDigestBytes() {
- return mInstantAppDigest.getDigestBytes();
- }
-
- public int[] getDigestPrefix() {
- return mInstantAppDigest.getDigestPrefix();
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeParcelable(mInstantAppDigest, flags);
- }
-
- @SuppressWarnings("hiding")
- public static final Parcelable.Creator<EphemeralDigest> CREATOR =
- new Parcelable.Creator<EphemeralDigest>() {
- @Override
- public EphemeralDigest createFromParcel(Parcel in) {
- return new EphemeralDigest(in);
- }
- @Override
- public EphemeralDigest[] newArray(int size) {
- return new EphemeralDigest[size];
- }
- };
- }
-}
diff --git a/core/java/android/content/pm/FeatureInfo.java b/core/java/android/content/pm/FeatureInfo.java
index 9ee6fa2431a3..ff9fd8ec31c5 100644
--- a/core/java/android/content/pm/FeatureInfo.java
+++ b/core/java/android/content/pm/FeatureInfo.java
@@ -18,6 +18,7 @@ package android.content.pm;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.proto.ProtoOutputStream;
/**
* Definition of a single optional hardware or software feature of an Android
@@ -113,6 +114,18 @@ public class FeatureInfo implements Parcelable {
dest.writeInt(flags);
}
+ /** @hide */
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ long token = proto.start(fieldId);
+ if (name != null) {
+ proto.write(FeatureInfoProto.NAME, name);
+ }
+ proto.write(FeatureInfoProto.VERSION, version);
+ proto.write(FeatureInfoProto.GLES_VERSION, getGlEsVersion());
+ proto.write(FeatureInfoProto.FLAGS, flags);
+ proto.end(token);
+ }
+
public static final Creator<FeatureInfo> CREATOR = new Creator<FeatureInfo>() {
@Override
public FeatureInfo createFromParcel(Parcel source) {
diff --git a/core/java/android/content/pm/ICrossProfileApps.aidl b/core/java/android/content/pm/ICrossProfileApps.aidl
new file mode 100644
index 000000000000..bc2f92a9bf51
--- /dev/null
+++ b/core/java/android/content/pm/ICrossProfileApps.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.app.IApplicationThread;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.UserHandle;
+
+/**
+ * @hide
+ */
+interface ICrossProfileApps {
+ void startActivityAsUser(in IApplicationThread caller, in String callingPackage,
+ in ComponentName component, in UserHandle user);
+ List<UserHandle> getTargetUserProfiles(in String callingPackage);
+} \ No newline at end of file
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index c08bd1db8302..ba7710b8ef48 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -16,6 +16,7 @@
package android.content.pm;
+import android.app.IApplicationThread;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentSender;
@@ -42,13 +43,14 @@ interface ILauncherApps {
String callingPackage, String packageName, in UserHandle user);
ActivityInfo resolveActivity(
String callingPackage, in ComponentName component, in UserHandle user);
- void startActivityAsUser(String callingPackage,
+ void startActivityAsUser(in IApplicationThread caller, String callingPackage,
in ComponentName component, in Rect sourceBounds,
in Bundle opts, in UserHandle user);
- void showAppDetailsAsUser(
+ void showAppDetailsAsUser(in IApplicationThread caller,
String callingPackage, in ComponentName component, in Rect sourceBounds,
in Bundle opts, in UserHandle user);
boolean isPackageEnabled(String callingPackage, String packageName, in UserHandle user);
+ Bundle getSuspendedPackageLauncherExtras(String packageName, in UserHandle user);
boolean isActivityEnabled(
String callingPackage, in ComponentName component, in UserHandle user);
ApplicationInfo getApplicationInfo(
diff --git a/core/java/android/content/pm/IOnAppsChangedListener.aidl b/core/java/android/content/pm/IOnAppsChangedListener.aidl
index e6525af311ed..fcb1de016078 100644
--- a/core/java/android/content/pm/IOnAppsChangedListener.aidl
+++ b/core/java/android/content/pm/IOnAppsChangedListener.aidl
@@ -17,6 +17,7 @@
package android.content.pm;
import android.content.pm.ParceledListSlice;
+import android.os.Bundle;
import android.os.UserHandle;
/**
@@ -28,7 +29,8 @@ oneway interface IOnAppsChangedListener {
void onPackageChanged(in UserHandle user, String packageName);
void onPackagesAvailable(in UserHandle user, in String[] packageNames, boolean replacing);
void onPackagesUnavailable(in UserHandle user, in String[] packageNames, boolean replacing);
- void onPackagesSuspended(in UserHandle user, in String[] packageNames);
+ void onPackagesSuspended(in UserHandle user, in String[] packageNames,
+ in Bundle launcherExtras);
void onPackagesUnsuspended(in UserHandle user, in String[] packageNames);
void onShortcutChanged(in UserHandle user, String packageName, in ParceledListSlice shortcuts);
}
diff --git a/core/java/android/content/pm/IPackageInstallObserver.aidl b/core/java/android/content/pm/IPackageInstallObserver.aidl
deleted file mode 100644
index 613336537317..000000000000
--- a/core/java/android/content/pm/IPackageInstallObserver.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
-**
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-package android.content.pm;
-
-/**
- * API for installation callbacks from the Package Manager.
- * @hide
- */
-oneway interface IPackageInstallObserver {
- void packageInstalled(in String packageName, int returnCode);
-}
-
diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl
index 0b16852246f8..8fddb99b35a8 100644
--- a/core/java/android/content/pm/IPackageInstallerSession.aidl
+++ b/core/java/android/content/pm/IPackageInstallerSession.aidl
@@ -26,9 +26,12 @@ interface IPackageInstallerSession {
void addClientProgress(float progress);
String[] getNames();
+
ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes);
ParcelFileDescriptor openRead(String name);
+ void write(String name, long offsetBytes, long lengthBytes, in ParcelFileDescriptor fd);
+
void removeSplit(String splitName);
void close();
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 92be900e71ce..c988fa907f86 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -26,7 +26,6 @@ import android.content.pm.ChangedPackages;
import android.content.pm.InstantAppInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.IDexModuleRegisterCallback;
-import android.content.pm.IPackageInstallObserver2;
import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageDeleteObserver2;
@@ -51,8 +50,8 @@ import android.content.pm.VersionedPackage;
import android.content.pm.dex.IArtManager;
import android.graphics.Bitmap;
import android.net.Uri;
-import android.os.Bundle;
import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
import android.content.IntentSender;
/**
@@ -222,13 +221,6 @@ interface IPackageManager {
ParceledListSlice queryInstrumentation(
String targetPackage, int flags);
- /** @deprecated Use PackageInstaller instead */
- void installPackageAsUser(in String originPath,
- in IPackageInstallObserver2 observer,
- int flags,
- in String installerPackageName,
- int userId);
-
void finishPackageInstall(int token, boolean didLaunch);
void setInstallerPackageName(in String targetPackage, in String installerPackageName);
@@ -280,9 +272,14 @@ interface IPackageManager {
void clearCrossProfileIntentFilters(int sourceUserId, String ownerPackage);
- String[] setPackagesSuspendedAsUser(in String[] packageNames, boolean suspended, int userId);
+ String[] setPackagesSuspendedAsUser(in String[] packageNames, boolean suspended,
+ in PersistableBundle appExtras, in PersistableBundle launcherExtras,
+ String dialogMessage, String callingPackage, int userId);
+
boolean isPackageSuspendedForUser(String packageName, int userId);
+ PersistableBundle getSuspendedPackageAppExtras(String packageName, int userId);
+
/**
* Backup/restore support - only the system uid may use these.
*/
@@ -544,9 +541,10 @@ interface IPackageManager {
void forceDexOpt(String packageName);
/**
- * Execute the background dexopt job immediately.
+ * Execute the background dexopt job immediately on packages in packageNames.
+ * If null, then execute on all packages.
*/
- boolean runBackgroundDexoptJob();
+ boolean runBackgroundDexoptJob(in List<String> packageNames);
/**
* Reconcile the information we have about the secondary dex files belonging to
@@ -555,14 +553,6 @@ interface IPackageManager {
*/
void reconcileSecondaryDexFiles(String packageName);
- /**
- * Update status of external media on the package manager to scan and
- * install packages installed on the external media. Like say the
- * StorageManagerService uses this to call into the package manager to update
- * status of sdcard.
- */
- void updateExternalMediaStatus(boolean mounted, boolean reportStatus);
-
PackageCleanItem nextPackageToClean(in PackageCleanItem lastPackage);
int getMoveStatus(int moveId);
@@ -622,6 +612,12 @@ interface IPackageManager {
void removeOnPermissionsChangeListener(in IOnPermissionsChangeListener listener);
void grantDefaultPermissionsToEnabledCarrierApps(in String[] packageNames, int userId);
void grantDefaultPermissionsToEnabledImsServices(in String[] packageNames, int userId);
+ void grantDefaultPermissionsToEnabledTelephonyDataServices(
+ in String[] packageNames, int userId);
+ void revokeDefaultPermissionsFromDisabledTelephonyDataServices(
+ in String[] packageNames, int userId);
+ void grantDefaultPermissionsToActiveLuiApp(in String packageName, int userId);
+ void revokeDefaultPermissionsFromLuiApps(in String[] packageNames, int userId);
boolean isPermissionRevokedByPolicy(String permission, String packageName, int userId);
@@ -665,4 +661,16 @@ interface IPackageManager {
String getInstantAppAndroidId(String packageName, int userId);
IArtManager getArtManager();
+
+ void setHarmfulAppWarning(String packageName, CharSequence warning, int userId);
+
+ CharSequence getHarmfulAppWarning(String packageName, int userId);
+
+ boolean hasSigningCertificate(String packageName, in byte[] signingCertificate, int flags);
+
+ boolean hasUidSigningCertificate(int uid, in byte[] signingCertificate, int flags);
+
+ String getSystemTextClassifierPackageName();
+
+ boolean isPackageStateProtected(String packageName, int userId);
}
diff --git a/core/java/android/content/pm/InstantAppRequest.java b/core/java/android/content/pm/InstantAppRequest.java
index 38f02256ee6e..361d4e4acb3b 100644
--- a/core/java/android/content/pm/InstantAppRequest.java
+++ b/core/java/android/content/pm/InstantAppRequest.java
@@ -18,12 +18,14 @@ package android.content.pm;
import android.content.Intent;
import android.os.Bundle;
+import android.text.TextUtils;
/**
* Information needed to make an instant application resolution request.
* @hide
*/
public final class InstantAppRequest {
+
/** Response from the first phase of instant application resolution */
public final AuxiliaryResolveInfo responseObj;
/** The original intent that triggered instant application resolution */
@@ -40,6 +42,8 @@ public final class InstantAppRequest {
public final Bundle verificationBundle;
/** Whether resolution occurs because an application is starting */
public final boolean resolveForStart;
+ /** The instant app digest for this request */
+ public final InstantAppResolveInfo.InstantAppDigest digest;
public InstantAppRequest(AuxiliaryResolveInfo responseObj, Intent origIntent,
String resolvedType, String callingPackage, int userId, Bundle verificationBundle,
@@ -51,5 +55,11 @@ public final class InstantAppRequest {
this.userId = userId;
this.verificationBundle = verificationBundle;
this.resolveForStart = resolveForStart;
+ if (origIntent.getData() != null && !TextUtils.isEmpty(origIntent.getData().getHost())) {
+ digest = new InstantAppResolveInfo.InstantAppDigest(
+ origIntent.getData().getHost(), 5 /*maxDigests*/);
+ } else {
+ digest = InstantAppResolveInfo.InstantAppDigest.UNDEFINED;
+ }
}
}
diff --git a/core/java/android/content/pm/InstantAppResolveInfo.java b/core/java/android/content/pm/InstantAppResolveInfo.java
index 22e994f4cfa8..8184361bca80 100644
--- a/core/java/android/content/pm/InstantAppResolveInfo.java
+++ b/core/java/android/content/pm/InstantAppResolveInfo.java
@@ -19,19 +19,46 @@ package android.content.pm;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.content.IntentFilter;
-import android.net.Uri;
+import android.content.Intent;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.Locale;
+import java.util.Random;
/**
- * Information about an instant application.
+ * Describes an externally resolvable instant application. There are three states that this class
+ * can represent: <p/>
+ * <ul>
+ * <li>
+ * The first, usable only for non http/s intents, implies that the resolver cannot
+ * immediately resolve this intent and would prefer that resolution be deferred to the
+ * instant app installer. Represent this state with {@link #InstantAppResolveInfo(Bundle)}.
+ * If the {@link android.content.Intent} has the scheme set to http/s and a set of digest
+ * prefixes were passed into one of the resolve methods in
+ * {@link android.app.InstantAppResolverService}, this state cannot be used.
+ * </li>
+ * <li>
+ * The second represents a partial match and is constructed with any of the other
+ * constructors. By setting one or more of the {@link Nullable}arguments to null, you
+ * communicate to the resolver in response to
+ * {@link android.app.InstantAppResolverService#onGetInstantAppResolveInfo(Intent, int[],
+ * String, InstantAppResolverService.InstantAppResolutionCallback)}
+ * that you need a 2nd round of resolution to complete the request.
+ * </li>
+ * <li>
+ * The third represents a complete match and is constructed with all @Nullable parameters
+ * populated.
+ * </li>
+ * </ul>
* @hide
*/
@SystemApi
@@ -39,15 +66,53 @@ public final class InstantAppResolveInfo implements Parcelable {
/** Algorithm that will be used to generate the domain digest */
private static final String SHA_ALGORITHM = "SHA-256";
+ private static final byte[] EMPTY_DIGEST = new byte[0];
+
private final InstantAppDigest mDigest;
private final String mPackageName;
/** The filters used to match domain */
private final List<InstantAppIntentFilter> mFilters;
/** The version code of the app that this class resolves to */
- private final int mVersionCode;
+ private final long mVersionCode;
+ /** Data about the app that should be passed along to the Instant App installer on resolve */
+ private final Bundle mExtras;
+ /**
+ * A flag that indicates that the resolver is aware that an app may match, but would prefer
+ * that the installer get the sanitized intent to decide.
+ */
+ private final boolean mShouldLetInstallerDecide;
+ /** Constructor for intent-based InstantApp resolution results. */
public InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName,
@Nullable List<InstantAppIntentFilter> filters, int versionCode) {
+ this(digest, packageName, filters, (long) versionCode, null /* extras */);
+ }
+
+ /** Constructor for intent-based InstantApp resolution results with extras. */
+ public InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName,
+ @Nullable List<InstantAppIntentFilter> filters, long versionCode,
+ @Nullable Bundle extras) {
+ this(digest, packageName, filters, versionCode, extras, false);
+ }
+
+ /** Constructor for intent-based InstantApp resolution results by hostname. */
+ public InstantAppResolveInfo(@NonNull String hostName, @Nullable String packageName,
+ @Nullable List<InstantAppIntentFilter> filters) {
+ this(new InstantAppDigest(hostName), packageName, filters, -1 /*versionCode*/,
+ null /* extras */);
+ }
+
+ /**
+ * Constructor that indicates that resolution could be delegated to the installer when the
+ * sanitized intent contains enough information to resolve completely.
+ */
+ public InstantAppResolveInfo(@Nullable Bundle extras) {
+ this(InstantAppDigest.UNDEFINED, null, null, -1, extras, true);
+ }
+
+ private InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName,
+ @Nullable List<InstantAppIntentFilter> filters, long versionCode,
+ @Nullable Bundle extras, boolean shouldLetInstallerDecide) {
// validate arguments
if ((packageName == null && (filters != null && filters.size() != 0))
|| (packageName != null && (filters == null || filters.size() == 0))) {
@@ -55,30 +120,45 @@ public final class InstantAppResolveInfo implements Parcelable {
}
mDigest = digest;
if (filters != null) {
- mFilters = new ArrayList<InstantAppIntentFilter>(filters.size());
+ mFilters = new ArrayList<>(filters.size());
mFilters.addAll(filters);
} else {
mFilters = null;
}
mPackageName = packageName;
mVersionCode = versionCode;
+ mExtras = extras;
+ mShouldLetInstallerDecide = shouldLetInstallerDecide;
}
- public InstantAppResolveInfo(@NonNull String hostName, @Nullable String packageName,
- @Nullable List<InstantAppIntentFilter> filters) {
- this(new InstantAppDigest(hostName), packageName, filters, -1 /*versionCode*/);
+ InstantAppResolveInfo(Parcel in) {
+ mShouldLetInstallerDecide = in.readBoolean();
+ mExtras = in.readBundle();
+ if (mShouldLetInstallerDecide) {
+ mDigest = InstantAppDigest.UNDEFINED;
+ mPackageName = null;
+ mFilters = Collections.emptyList();
+ mVersionCode = -1;
+ } else {
+ mDigest = in.readParcelable(null /*loader*/);
+ mPackageName = in.readString();
+ mFilters = new ArrayList<>();
+ in.readList(mFilters, null /*loader*/);
+ mVersionCode = in.readLong();
+ }
}
- InstantAppResolveInfo(Parcel in) {
- mDigest = in.readParcelable(null /*loader*/);
- mPackageName = in.readString();
- mFilters = new ArrayList<InstantAppIntentFilter>();
- in.readList(mFilters, null /*loader*/);
- mVersionCode = in.readInt();
+ /**
+ * Returns true if the resolver is aware that an app may match, but would prefer
+ * that the installer get the sanitized intent to decide. This should not be true for
+ * resolutions that include a host and will be ignored in such cases.
+ */
+ public boolean shouldLetInstallerDecide() {
+ return mShouldLetInstallerDecide;
}
public byte[] getDigestBytes() {
- return mDigest.getDigestBytes()[0];
+ return mDigest.mDigestBytes.length > 0 ? mDigest.getDigestBytes()[0] : EMPTY_DIGEST;
}
public int getDigestPrefix() {
@@ -93,10 +173,23 @@ public final class InstantAppResolveInfo implements Parcelable {
return mFilters;
}
+ /**
+ * @deprecated Use {@link #getLongVersionCode} instead.
+ */
+ @Deprecated
public int getVersionCode() {
+ return (int) (mVersionCode & 0xffffffff);
+ }
+
+ public long getLongVersionCode() {
return mVersionCode;
}
+ @Nullable
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -104,10 +197,15 @@ public final class InstantAppResolveInfo implements Parcelable {
@Override
public void writeToParcel(Parcel out, int flags) {
+ out.writeBoolean(mShouldLetInstallerDecide);
+ out.writeBundle(mExtras);
+ if (mShouldLetInstallerDecide) {
+ return;
+ }
out.writeParcelable(mDigest, flags);
out.writeString(mPackageName);
out.writeList(mFilters);
- out.writeInt(mVersionCode);
+ out.writeLong(mVersionCode);
}
public static final Parcelable.Creator<InstantAppResolveInfo> CREATOR
@@ -134,12 +232,30 @@ public final class InstantAppResolveInfo implements Parcelable {
*/
@SystemApi
public static final class InstantAppDigest implements Parcelable {
- private static final int DIGEST_MASK = 0xfffff000;
- private static final int DIGEST_PREFIX_COUNT = 5;
+ static final int DIGEST_MASK = 0xfffff000;
+
+ /**
+ * A special instance that represents and undefined digest used for cases that a host was
+ * not provided or is irrelevant to the response.
+ */
+ public static final InstantAppDigest UNDEFINED =
+ new InstantAppDigest(new byte[][]{}, new int[]{});
+
+ private static Random sRandom = null;
+ static {
+ try {
+ sRandom = SecureRandom.getInstance("SHA1PRNG");
+ } catch (NoSuchAlgorithmException e) {
+ // oh well
+ sRandom = new Random();
+ }
+ }
/** Full digest of the domain hashes */
private final byte[][] mDigestBytes;
- /** The first 4 bytes of the domain hashes */
+ /** The first 5 bytes of the domain hashes */
private final int[] mDigestPrefix;
+ /** The first 5 bytes of the domain hashes interspersed with random data */
+ private int[] mDigestPrefixSecure;
public InstantAppDigest(@NonNull String hostName) {
this(hostName, -1 /*maxDigests*/);
@@ -162,6 +278,11 @@ public final class InstantAppResolveInfo implements Parcelable {
}
}
+ private InstantAppDigest(byte[][] digestBytes, int[] prefix) {
+ this.mDigestPrefix = prefix;
+ this.mDigestBytes = digestBytes;
+ }
+
private static byte[][] generateDigest(String hostName, int maxDigests) {
ArrayList<byte[]> digests = new ArrayList<>();
try {
@@ -206,6 +327,7 @@ public final class InstantAppResolveInfo implements Parcelable {
}
}
mDigestPrefix = in.createIntArray();
+ mDigestPrefixSecure = in.createIntArray();
}
public byte[][] getDigestBytes() {
@@ -216,6 +338,26 @@ public final class InstantAppResolveInfo implements Parcelable {
return mDigestPrefix;
}
+ /**
+ * Returns a digest prefix with additional random prefixes interspersed.
+ * @hide
+ */
+ public int[] getDigestPrefixSecure() {
+ if (this == InstantAppResolveInfo.InstantAppDigest.UNDEFINED) {
+ return getDigestPrefix();
+ } else if (mDigestPrefixSecure == null) {
+ // let's generate some random data to intersperse throughout the set of prefixes
+ final int realSize = getDigestPrefix().length;
+ final int manufacturedSize = realSize + 10 + sRandom.nextInt(10);
+ mDigestPrefixSecure = Arrays.copyOf(getDigestPrefix(), manufacturedSize);
+ for (int i = realSize; i < manufacturedSize; i++) {
+ mDigestPrefixSecure[i] = sRandom.nextInt() & DIGEST_MASK;
+ }
+ Arrays.sort(mDigestPrefixSecure);
+ }
+ return mDigestPrefixSecure;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -223,6 +365,11 @@ public final class InstantAppResolveInfo implements Parcelable {
@Override
public void writeToParcel(Parcel out, int flags) {
+ final boolean isUndefined = this == UNDEFINED;
+ out.writeBoolean(isUndefined);
+ if (isUndefined) {
+ return;
+ }
if (mDigestBytes == null) {
out.writeInt(-1);
} else {
@@ -232,6 +379,7 @@ public final class InstantAppResolveInfo implements Parcelable {
}
}
out.writeIntArray(mDigestPrefix);
+ out.writeIntArray(mDigestPrefixSecure);
}
@SuppressWarnings("hiding")
@@ -239,6 +387,9 @@ public final class InstantAppResolveInfo implements Parcelable {
new Parcelable.Creator<InstantAppDigest>() {
@Override
public InstantAppDigest createFromParcel(Parcel in) {
+ if (in.readBoolean() /* is undefined */) {
+ return UNDEFINED;
+ }
return new InstantAppDigest(in);
}
@Override
diff --git a/core/java/android/content/pm/InstrumentationInfo.java b/core/java/android/content/pm/InstrumentationInfo.java
index 3faa9517d17a..fb2e4a046c9f 100644
--- a/core/java/android/content/pm/InstrumentationInfo.java
+++ b/core/java/android/content/pm/InstrumentationInfo.java
@@ -101,6 +101,12 @@ public class InstrumentationInfo extends PackageItemInfo implements Parcelable {
/** {@hide} */
public String credentialProtectedDataDir;
+ /** {@hide} */
+ public String primaryCpuAbi;
+
+ /** {@hide} */
+ public String secondaryCpuAbi;
+
/** {@hide} Full path to the directory containing primary ABI native libraries. */
public String nativeLibraryDir;
@@ -131,6 +137,8 @@ public class InstrumentationInfo extends PackageItemInfo implements Parcelable {
dataDir = orig.dataDir;
deviceProtectedDataDir = orig.deviceProtectedDataDir;
credentialProtectedDataDir = orig.credentialProtectedDataDir;
+ primaryCpuAbi = orig.primaryCpuAbi;
+ secondaryCpuAbi = orig.secondaryCpuAbi;
nativeLibraryDir = orig.nativeLibraryDir;
secondaryNativeLibraryDir = orig.secondaryNativeLibraryDir;
handleProfiling = orig.handleProfiling;
@@ -160,6 +168,8 @@ public class InstrumentationInfo extends PackageItemInfo implements Parcelable {
dest.writeString(dataDir);
dest.writeString(deviceProtectedDataDir);
dest.writeString(credentialProtectedDataDir);
+ dest.writeString(primaryCpuAbi);
+ dest.writeString(secondaryCpuAbi);
dest.writeString(nativeLibraryDir);
dest.writeString(secondaryNativeLibraryDir);
dest.writeInt((handleProfiling == false) ? 0 : 1);
@@ -190,6 +200,8 @@ public class InstrumentationInfo extends PackageItemInfo implements Parcelable {
dataDir = source.readString();
deviceProtectedDataDir = source.readString();
credentialProtectedDataDir = source.readString();
+ primaryCpuAbi = source.readString();
+ secondaryCpuAbi = source.readString();
nativeLibraryDir = source.readString();
secondaryNativeLibraryDir = source.readString();
handleProfiling = source.readInt() != 0;
@@ -208,6 +220,8 @@ public class InstrumentationInfo extends PackageItemInfo implements Parcelable {
ai.dataDir = dataDir;
ai.deviceProtectedDataDir = deviceProtectedDataDir;
ai.credentialProtectedDataDir = credentialProtectedDataDir;
+ ai.primaryCpuAbi = primaryCpuAbi;
+ ai.secondaryCpuAbi = secondaryCpuAbi;
ai.nativeLibraryDir = nativeLibraryDir;
ai.secondaryNativeLibraryDir = secondaryNativeLibraryDir;
}
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index aa9562ff040f..fa423e29406a 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -20,8 +20,8 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
-import android.annotation.SystemService;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemService;
import android.annotation.TestApi;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
@@ -37,10 +37,10 @@ import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Rect;
+import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
-import android.graphics.drawable.AdaptiveIconDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -211,6 +211,10 @@ public class LauncherApps {
* example, this can happen when a Device Administrator suspends
* an applicaton.
*
+ * <p>Note: On devices running {@link android.os.Build.VERSION_CODES#P Android P} or higher,
+ * any apps that override {@link #onPackagesSuspended(String[], UserHandle, Bundle)} will
+ * not receive this callback.
+ *
* @param packageNames The names of the packages that have just been
* suspended.
* @param user The UserHandle of the profile that generated the change.
@@ -219,6 +223,27 @@ public class LauncherApps {
}
/**
+ * Indicates that one or more packages have been suspended. A device administrator or an app
+ * with {@code android.permission.SUSPEND_APPS} can do this.
+ *
+ * <p>A suspending app with the permission {@code android.permission.SUSPEND_APPS} can
+ * optionally provide a {@link Bundle} of extra information that it deems helpful for the
+ * launcher to handle the suspended state of these packages. The contents of this
+ * {@link Bundle} are supposed to be a contract between the suspending app and the launcher.
+ *
+ * @param packageNames The names of the packages that have just been suspended.
+ * @param user the user for which the given packages were suspended.
+ * @param launcherExtras A {@link Bundle} of extras for the launcher, if provided to the
+ * system, {@code null} otherwise.
+ * @see PackageManager#isPackageSuspended()
+ * @see #getSuspendedPackageLauncherExtras(String, UserHandle)
+ */
+ public void onPackagesSuspended(String[] packageNames, UserHandle user,
+ @Nullable Bundle launcherExtras) {
+ onPackagesSuspended(packageNames, user);
+ }
+
+ /**
* Indicates that one or more packages have been unsuspended. For
* example, this can happen when a Device Administrator unsuspends
* an applicaton.
@@ -265,6 +290,14 @@ public class LauncherApps {
/**
* Include pinned shortcuts in the result.
+ *
+ * <p>If you are the selected assistant app, and wishes to fetch all shortcuts that the
+ * user owns on the launcher (or by other launchers, in case the user has multiple), use
+ * {@link #FLAG_MATCH_PINNED_BY_ANY_LAUNCHER} instead.
+ *
+ * <p>If you're a regular launcher app, there's no way to get shortcuts pinned by other
+ * launchers, and {@link #FLAG_MATCH_PINNED_BY_ANY_LAUNCHER} will be ignored. So use this
+ * flag to get own pinned shortcuts.
*/
public static final int FLAG_MATCH_PINNED = 1 << 1;
@@ -282,12 +315,34 @@ public class LauncherApps {
public static final int FLAG_GET_MANIFEST = FLAG_MATCH_MANIFEST;
/**
- * Does not retrieve CHOOSER only shortcuts.
- * TODO: Add another flag for MATCH_ALL_PINNED
+ * Include all pinned shortcuts by any launchers, not just by the caller,
+ * in the result.
+ *
+ * <p>The caller must be the selected assistant app to use this flag, or have the system
+ * {@code ACCESS_SHORTCUTS} permission.
+ *
+ * <p>If you are the selected assistant app, and wishes to fetch all shortcuts that the
+ * user owns on the launcher (or by other launchers, in case the user has multiple), use
+ * {@link #FLAG_MATCH_PINNED_BY_ANY_LAUNCHER} instead.
+ *
+ * <p>If you're a regular launcher app (or any app that's not the selected assistant app)
+ * then this flag will be ignored.
+ */
+ public static final int FLAG_MATCH_PINNED_BY_ANY_LAUNCHER = 1 << 10;
+
+ /**
+ * FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_MANIFEST
* @hide
*/
public static final int FLAG_MATCH_ALL_KINDS =
- FLAG_GET_DYNAMIC | FLAG_GET_PINNED | FLAG_GET_MANIFEST;
+ FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_MANIFEST;
+
+ /**
+ * FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_MANIFEST | FLAG_MATCH_ALL_PINNED
+ * @hide
+ */
+ public static final int FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED =
+ FLAG_MATCH_ALL_KINDS | FLAG_MATCH_PINNED_BY_ANY_LAUNCHER;
/** @hide kept for unit tests */
@Deprecated
@@ -313,13 +368,13 @@ public class LauncherApps {
public static final int FLAG_GET_KEY_FIELDS_ONLY = 1 << 2;
/** @hide */
- @IntDef(flag = true,
- value = {
- FLAG_MATCH_DYNAMIC,
- FLAG_MATCH_PINNED,
- FLAG_MATCH_MANIFEST,
- FLAG_GET_KEY_FIELDS_ONLY,
- })
+ @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+ FLAG_MATCH_DYNAMIC,
+ FLAG_MATCH_PINNED,
+ FLAG_MATCH_MANIFEST,
+ FLAG_GET_KEY_FIELDS_ONLY,
+ FLAG_MATCH_MANIFEST,
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface QueryFlags {}
@@ -493,7 +548,8 @@ public class LauncherApps {
Log.i(TAG, "StartMainActivity " + component + " " + user.getIdentifier());
}
try {
- mService.startActivityAsUser(mContext.getPackageName(),
+ mService.startActivityAsUser(mContext.getIApplicationThread(),
+ mContext.getPackageName(),
component, sourceBounds, opts, user);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
@@ -513,7 +569,8 @@ public class LauncherApps {
Rect sourceBounds, Bundle opts) {
logErrorForInvalidProfileAccess(user);
try {
- mService.showAppDetailsAsUser(mContext.getPackageName(),
+ mService.showAppDetailsAsUser(mContext.getIApplicationThread(),
+ mContext.getPackageName(),
component, sourceBounds, opts, user);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
@@ -608,6 +665,34 @@ public class LauncherApps {
}
/**
+ * Gets the launcher extras supplied to the system when the given package was suspended via
+ * {@code PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
+ * PersistableBundle, String)}.
+ *
+ * <p>The contents of this {@link Bundle} are supposed to be a contract between the suspending
+ * app and the launcher.
+ *
+ * <p>Note: This just returns whatever extras were provided to the system, <em>which might
+ * even be {@code null}.</em>
+ *
+ * @param packageName The package for which to fetch the launcher extras.
+ * @param user The {@link UserHandle} of the profile.
+ * @return A {@link Bundle} of launcher extras. Or {@code null} if the package is not currently
+ * suspended.
+ *
+ * @see Callback#onPackagesSuspended(String[], UserHandle, Bundle)
+ * @see PackageManager#isPackageSuspended()
+ */
+ public @Nullable Bundle getSuspendedPackageLauncherExtras(String packageName, UserHandle user) {
+ logErrorForInvalidProfileAccess(user);
+ try {
+ return mService.getSuspendedPackageLauncherExtras(packageName, user);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns {@link ApplicationInfo} about an application installed for a specific user profile.
*
* @param packageName The package name of the application
@@ -622,7 +707,7 @@ public class LauncherApps {
@ApplicationInfoFlags int flags, @NonNull UserHandle user)
throws PackageManager.NameNotFoundException {
Preconditions.checkNotNull(packageName, "packageName");
- Preconditions.checkNotNull(packageName, "user");
+ Preconditions.checkNotNull(user, "user");
logErrorForInvalidProfileAccess(user);
try {
final ApplicationInfo ai = mService
@@ -655,9 +740,13 @@ public class LauncherApps {
}
/**
- * Returns whether the caller can access the shortcut information.
+ * Returns whether the caller can access the shortcut information. Access is currently
+ * available to:
*
- * <p>Only the default launcher can access the shortcut information.
+ * <ul>
+ * <li>The current launcher (or default launcher if there is no set current launcher).</li>
+ * <li>The currently active voice interaction service.</li>
+ * </ul>
*
* <p>Note when this method returns {@code false}, it may be a temporary situation because
* the user is trying a new launcher application. The user may decide to change the default
@@ -678,6 +767,21 @@ public class LauncherApps {
}
}
+ private List<ShortcutInfo> maybeUpdateDisabledMessage(List<ShortcutInfo> shortcuts) {
+ if (shortcuts == null) {
+ return null;
+ }
+ for (int i = shortcuts.size() - 1; i >= 0; i--) {
+ final ShortcutInfo si = shortcuts.get(i);
+ final String message = ShortcutInfo.getDisabledReasonForRestoreIssue(mContext,
+ si.getDisabledReason());
+ if (message != null) {
+ si.setDisabledMessage(message);
+ }
+ }
+ return shortcuts;
+ }
+
/**
* Returns {@link ShortcutInfo}s that match {@code query}.
*
@@ -698,10 +802,16 @@ public class LauncherApps {
@NonNull UserHandle user) {
logErrorForInvalidProfileAccess(user);
try {
- return mService.getShortcuts(mContext.getPackageName(),
+ // Note this is the only case we need to update the disabled message for shortcuts
+ // that weren't restored.
+ // The restore problem messages are only shown by the user, and publishers will never
+ // see them. The only other API that the launcher gets shortcuts is the shortcut
+ // changed callback, but that only returns shortcuts with the "key" information, so
+ // that won't return disabled message.
+ return maybeUpdateDisabledMessage(mService.getShortcuts(mContext.getPackageName(),
query.mChangedSince, query.mPackage, query.mShortcutIds, query.mActivity,
query.mQueryFlags, user)
- .getList();
+ .getList());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1108,14 +1218,15 @@ public class LauncherApps {
}
@Override
- public void onPackagesSuspended(UserHandle user, String[] packageNames)
+ public void onPackagesSuspended(UserHandle user, String[] packageNames,
+ Bundle launcherExtras)
throws RemoteException {
if (DEBUG) {
Log.d(TAG, "onPackagesSuspended " + user.getIdentifier() + "," + packageNames);
}
synchronized (LauncherApps.this) {
for (CallbackMessageHandler callback : mCallbacks) {
- callback.postOnPackagesSuspended(packageNames, user);
+ callback.postOnPackagesSuspended(packageNames, launcherExtras, user);
}
}
}
@@ -1163,6 +1274,7 @@ public class LauncherApps {
private static class CallbackInfo {
String[] packageNames;
String packageName;
+ Bundle launcherExtras;
boolean replacing;
UserHandle user;
List<ShortcutInfo> shortcuts;
@@ -1196,7 +1308,8 @@ public class LauncherApps {
mCallback.onPackagesUnavailable(info.packageNames, info.user, info.replacing);
break;
case MSG_SUSPENDED:
- mCallback.onPackagesSuspended(info.packageNames, info.user);
+ mCallback.onPackagesSuspended(info.packageNames, info.user, info.launcherExtras
+ );
break;
case MSG_UNSUSPENDED:
mCallback.onPackagesUnsuspended(info.packageNames, info.user);
@@ -1246,10 +1359,12 @@ public class LauncherApps {
obtainMessage(MSG_UNAVAILABLE, info).sendToTarget();
}
- public void postOnPackagesSuspended(String[] packageNames, UserHandle user) {
+ public void postOnPackagesSuspended(String[] packageNames, Bundle launcherExtras,
+ UserHandle user) {
CallbackInfo info = new CallbackInfo();
info.packageNames = packageNames;
info.user = user;
+ info.launcherExtras = launcherExtras;
obtainMessage(MSG_SUSPENDED, info).sendToTarget();
}
@@ -1324,7 +1439,10 @@ public class LauncherApps {
public static final int REQUEST_TYPE_APPWIDGET = 2;
/** @hide */
- @IntDef(value = {REQUEST_TYPE_SHORTCUT})
+ @IntDef(prefix = { "REQUEST_TYPE_" }, value = {
+ REQUEST_TYPE_SHORTCUT,
+ REQUEST_TYPE_APPWIDGET
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface RequestType {}
@@ -1371,6 +1489,10 @@ public class LauncherApps {
* Always non-null for a {@link #REQUEST_TYPE_APPWIDGET} request, and always null for a
* different request type.
*
+ * <p>Launcher should not show any configuration activity associated with the provider, and
+ * assume that the widget is already fully configured. Upon accepting the widget, it should
+ * pass the widgetId in {@link #accept(Bundle)}.
+ *
* @return requested {@link AppWidgetProviderInfo} when a request is of the
* {@link #REQUEST_TYPE_APPWIDGET} type. Null otherwise.
*/
diff --git a/core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java b/core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java
new file mode 100644
index 000000000000..81e4105febee
--- /dev/null
+++ b/core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+package android.content.pm;
+
+import static android.content.pm.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
+
+import android.content.pm.PackageParser.Package;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Updates a package to ensure that if it targets < P that the org.apache.http.legacy library is
+ * included by default.
+ *
+ * <p>This is separated out so that it can be conditionally included at build time depending on
+ * whether org.apache.http.legacy is on the bootclasspath or not. In order to include this at
+ * build time, and remove org.apache.http.legacy from the bootclasspath pass
+ * REMOVE_OAHL_FROM_BCP=true on the build command line, otherwise this class will not be included
+ * and the
+ *
+ * @hide
+ */
+@VisibleForTesting
+public class OrgApacheHttpLegacyUpdater extends PackageSharedLibraryUpdater {
+
+ @Override
+ public void updatePackage(Package pkg) {
+ // Packages targeted at <= O_MR1 expect the classes in the org.apache.http.legacy library
+ // to be accessible so this maintains backward compatibility by adding the
+ // org.apache.http.legacy library to those packages.
+ if (apkTargetsApiLevelLessThanOrEqualToOMR1(pkg)) {
+ prefixRequiredLibrary(pkg, ORG_APACHE_HTTP_LEGACY);
+ }
+ }
+}
diff --git a/core/java/android/content/pm/PackageBackwardCompatibility.java b/core/java/android/content/pm/PackageBackwardCompatibility.java
index 4de160b0bf88..a16f81b11ae6 100644
--- a/core/java/android/content/pm/PackageBackwardCompatibility.java
+++ b/core/java/android/content/pm/PackageBackwardCompatibility.java
@@ -16,13 +16,19 @@
package android.content.pm;
-import android.annotation.Nullable;
+import static android.content.pm.SharedLibraryNames.ANDROID_TEST_BASE;
+import static android.content.pm.SharedLibraryNames.ANDROID_TEST_MOCK;
+import static android.content.pm.SharedLibraryNames.ANDROID_TEST_RUNNER;
+import static android.content.pm.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
+
import android.content.pm.PackageParser.Package;
+import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Supplier;
/**
* Modifies {@link Package} in order to maintain backwards compatibility.
@@ -30,11 +36,96 @@ import java.util.ArrayList;
* @hide
*/
@VisibleForTesting
-public class PackageBackwardCompatibility {
+public class PackageBackwardCompatibility extends PackageSharedLibraryUpdater {
+
+ private static final String TAG = PackageBackwardCompatibility.class.getSimpleName();
+
+ private static final PackageBackwardCompatibility INSTANCE;
+
+ static {
+ final List<PackageSharedLibraryUpdater> packageUpdaters = new ArrayList<>();
+
+ // Attempt to load and add the optional updater that will only be available when
+ // REMOVE_OAHL_FROM_BCP=true. If that could not be found then add the default updater that
+ // will remove any references to org.apache.http.library from the package so that it does
+ // not try and load the library when it is on the bootclasspath.
+ boolean bootClassPathContainsOAHL = !addOptionalUpdater(packageUpdaters,
+ "android.content.pm.OrgApacheHttpLegacyUpdater",
+ RemoveUnnecessaryOrgApacheHttpLegacyLibrary::new);
+
+ // Add this before adding AndroidTestBaseUpdater so that android.test.base comes before
+ // android.test.mock.
+ packageUpdaters.add(new AndroidTestRunnerSplitUpdater());
+
+ // Attempt to load and add the optional updater that will only be available when
+ // REMOVE_ATB_FROM_BCP=true. If that could not be found then add the default updater that
+ // will remove any references to org.apache.http.library from the package so that it does
+ // not try and load the library when it is on the bootclasspath.
+ boolean bootClassPathContainsATB = !addOptionalUpdater(packageUpdaters,
+ "android.content.pm.AndroidTestBaseUpdater",
+ RemoveUnnecessaryAndroidTestBaseLibrary::new);
+
+ PackageSharedLibraryUpdater[] updaterArray = packageUpdaters
+ .toArray(new PackageSharedLibraryUpdater[0]);
+ INSTANCE = new PackageBackwardCompatibility(
+ bootClassPathContainsOAHL, bootClassPathContainsATB, updaterArray);
+ }
+
+ /**
+ * Add an optional {@link PackageSharedLibraryUpdater} instance to the list, if it could not be
+ * found then add a default instance instead.
+ *
+ * @param packageUpdaters the list to update.
+ * @param className the name of the optional class.
+ * @param defaultUpdater the supplier of the default instance.
+ * @return true if the optional updater was added false otherwise.
+ */
+ private static boolean addOptionalUpdater(List<PackageSharedLibraryUpdater> packageUpdaters,
+ String className, Supplier<PackageSharedLibraryUpdater> defaultUpdater) {
+ Class<? extends PackageSharedLibraryUpdater> clazz;
+ try {
+ clazz = (PackageBackwardCompatibility.class.getClassLoader()
+ .loadClass(className)
+ .asSubclass(PackageSharedLibraryUpdater.class));
+ Log.i(TAG, "Loaded " + className);
+ } catch (ClassNotFoundException e) {
+ Log.i(TAG, "Could not find " + className + ", ignoring");
+ clazz = null;
+ }
- private static final String ANDROID_TEST_MOCK = "android.test.mock";
+ boolean usedOptional = false;
+ PackageSharedLibraryUpdater updater;
+ if (clazz == null) {
+ updater = defaultUpdater.get();
+ } else {
+ try {
+ updater = clazz.getConstructor().newInstance();
+ usedOptional = true;
+ } catch (ReflectiveOperationException e) {
+ throw new IllegalStateException("Could not create instance of " + className, e);
+ }
+ }
+ packageUpdaters.add(updater);
+ return usedOptional;
+ }
+
+ @VisibleForTesting
+ public static PackageSharedLibraryUpdater getInstance() {
+ return INSTANCE;
+ }
+
+ private final boolean mBootClassPathContainsOAHL;
+
+ private final boolean mBootClassPathContainsATB;
- private static final String ANDROID_TEST_RUNNER = "android.test.runner";
+ private final PackageSharedLibraryUpdater[] mPackageUpdaters;
+
+ public PackageBackwardCompatibility(boolean bootClassPathContainsOAHL,
+ boolean bootClassPathContainsATB, PackageSharedLibraryUpdater[] packageUpdaters) {
+ this.mBootClassPathContainsOAHL = bootClassPathContainsOAHL;
+ this.mBootClassPathContainsATB = bootClassPathContainsATB;
+ this.mPackageUpdaters = packageUpdaters;
+ }
/**
* Modify the shared libraries in the supplied {@link Package} to maintain backwards
@@ -44,35 +135,78 @@ public class PackageBackwardCompatibility {
*/
@VisibleForTesting
public static void modifySharedLibraries(Package pkg) {
- ArrayList<String> usesLibraries = pkg.usesLibraries;
- ArrayList<String> usesOptionalLibraries = pkg.usesOptionalLibraries;
-
- usesLibraries = orgApacheHttpLegacy(usesLibraries);
- usesOptionalLibraries = orgApacheHttpLegacy(usesOptionalLibraries);
-
- // android.test.runner has a dependency on android.test.mock so if android.test.runner
- // is present but android.test.mock is not then add android.test.mock.
- boolean androidTestMockPresent = ArrayUtils.contains(usesLibraries, ANDROID_TEST_MOCK)
- || ArrayUtils.contains(usesOptionalLibraries, ANDROID_TEST_MOCK);
- if (ArrayUtils.contains(usesLibraries, ANDROID_TEST_RUNNER) && !androidTestMockPresent) {
- usesLibraries.add(ANDROID_TEST_MOCK);
+ INSTANCE.updatePackage(pkg);
+ }
+
+ @Override
+ public void updatePackage(Package pkg) {
+ for (PackageSharedLibraryUpdater packageUpdater : mPackageUpdaters) {
+ packageUpdater.updatePackage(pkg);
}
- if (ArrayUtils.contains(usesOptionalLibraries, ANDROID_TEST_RUNNER)
- && !androidTestMockPresent) {
- usesOptionalLibraries.add(ANDROID_TEST_MOCK);
+ }
+
+ /**
+ * True if the org.apache.http.legacy is on the bootclasspath, false otherwise.
+ */
+ @VisibleForTesting
+ public static boolean bootClassPathContainsOAHL() {
+ return INSTANCE.mBootClassPathContainsOAHL;
+ }
+
+ /**
+ * True if the android.test.base is on the bootclasspath, false otherwise.
+ */
+ @VisibleForTesting
+ public static boolean bootClassPathContainsATB() {
+ return INSTANCE.mBootClassPathContainsATB;
+ }
+
+ /**
+ * Add android.test.mock dependency for any APK that depends on android.test.runner.
+ *
+ * <p>This is needed to maintain backwards compatibility as in previous versions of Android the
+ * android.test.runner library included the classes from android.test.mock which have since
+ * been split out into a separate library.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public static class AndroidTestRunnerSplitUpdater extends PackageSharedLibraryUpdater {
+
+ @Override
+ public void updatePackage(Package pkg) {
+ // android.test.runner has a dependency on android.test.mock so if android.test.runner
+ // is present but android.test.mock is not then add android.test.mock.
+ prefixImplicitDependency(pkg, ANDROID_TEST_RUNNER, ANDROID_TEST_MOCK);
+ }
+ }
+
+ /**
+ * Remove any usages of org.apache.http.legacy from the shared library as the library is on the
+ * bootclasspath.
+ */
+ @VisibleForTesting
+ public static class RemoveUnnecessaryOrgApacheHttpLegacyLibrary
+ extends PackageSharedLibraryUpdater {
+
+ @Override
+ public void updatePackage(Package pkg) {
+ removeLibrary(pkg, ORG_APACHE_HTTP_LEGACY);
}
- pkg.usesLibraries = usesLibraries;
- pkg.usesOptionalLibraries = usesOptionalLibraries;
}
- private static ArrayList<String> orgApacheHttpLegacy(@Nullable ArrayList<String> libraries) {
- // "org.apache.http.legacy" is now a part of the boot classpath so it doesn't need
- // to be an explicit dependency.
- //
- // A future change will remove this library from the boot classpath, at which point
- // all apps that target SDK 21 and earlier will have it automatically added to their
- // dependency lists.
- return ArrayUtils.remove(libraries, "org.apache.http.legacy");
+ /**
+ * Remove any usages of android.test.base from the shared library as the library is on the
+ * bootclasspath.
+ */
+ @VisibleForTesting
+ public static class RemoveUnnecessaryAndroidTestBaseLibrary
+ extends PackageSharedLibraryUpdater {
+
+ @Override
+ public void updatePackage(Package pkg) {
+ removeLibrary(pkg, ANDROID_TEST_BASE);
+ }
}
}
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index ba488f6a0518..5d8122f65c71 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -16,6 +16,7 @@
package android.content.pm;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -36,13 +37,56 @@ public class PackageInfo implements Parcelable {
public String[] splitNames;
/**
+ * @deprecated Use {@link #getLongVersionCode()} instead, which includes both
+ * this and the additional
+ * {@link android.R.styleable#AndroidManifest_versionCodeMajor versionCodeMajor} attribute.
* The version number of this package, as specified by the &lt;manifest&gt;
* tag's {@link android.R.styleable#AndroidManifest_versionCode versionCode}
* attribute.
+ * @see #getLongVersionCode()
*/
+ @Deprecated
public int versionCode;
/**
+ * @hide
+ * The major version number of this package, as specified by the &lt;manifest&gt;
+ * tag's {@link android.R.styleable#AndroidManifest_versionCode versionCodeMajor}
+ * attribute.
+ * @see #getLongVersionCode()
+ */
+ public int versionCodeMajor;
+
+ /**
+ * Return {@link android.R.styleable#AndroidManifest_versionCode versionCode} and
+ * {@link android.R.styleable#AndroidManifest_versionCodeMajor versionCodeMajor} combined
+ * together as a single long value. The
+ * {@link android.R.styleable#AndroidManifest_versionCodeMajor versionCodeMajor} is placed in
+ * the upper 32 bits.
+ */
+ public long getLongVersionCode() {
+ return composeLongVersionCode(versionCodeMajor, versionCode);
+ }
+
+ /**
+ * Set the full version code in this PackageInfo, updating {@link #versionCode}
+ * with the lower bits.
+ * @see #getLongVersionCode()
+ */
+ public void setLongVersionCode(long longVersionCode) {
+ versionCodeMajor = (int) (longVersionCode>>32);
+ versionCode = (int) longVersionCode;
+ }
+
+ /**
+ * @hide Internal implementation for composing a minor and major version code in to
+ * a single long version code.
+ */
+ public static long composeLongVersionCode(int major, int minor) {
+ return (((long) major) << 32) | (((long) minor) & 0xffffffffL);
+ }
+
+ /**
* The version name of this package, as specified by the &lt;manifest&gt;
* tag's {@link android.R.styleable#AndroidManifest_versionName versionName}
* attribute.
@@ -198,9 +242,30 @@ public class PackageInfo implements Parcelable {
* equivalent to being signed with certificates B and A. This means that
* in case multiple signatures are reported you cannot assume the one at
* the first position to be the same across updates.
+ *
+ * <strong>Deprecated</strong> This has been replaced by the
+ * {@link PackageInfo#signingInfo} field, which takes into
+ * account signing certificate rotation. For backwards compatibility in
+ * the event of signing certificate rotation, this will return the oldest
+ * reported signing certificate, so that an application will appear to
+ * callers as though no rotation occurred.
+ *
+ * @deprecated use {@code signingInfo} instead
*/
+ @Deprecated
public Signature[] signatures;
-
+
+ /**
+ * Signing information read from the package file, potentially
+ * including past signing certificates no longer used after signing
+ * certificate rotation. This is only filled in if
+ * the flag {@link PackageManager#GET_SIGNING_CERTIFICATES} was set.
+ *
+ * Use this field instead of the deprecated {@code signatures} field.
+ * See {@link SigningInfo} for more information on its contents.
+ */
+ public SigningInfo signingInfo;
+
/**
* Application specified preferred configuration
* {@link android.R.styleable#AndroidManifestUsesConfiguration
@@ -283,15 +348,64 @@ public class PackageInfo implements Parcelable {
*/
public String overlayTarget;
+ /**
+ * The overlay category, if any, of this package
+ *
+ * @hide
+ */
+ public String overlayCategory;
+
/** @hide */
public int overlayPriority;
- /** @hide */
- public boolean isStaticOverlay;
+ /**
+ * Whether the overlay is static, meaning it cannot be enabled/disabled at runtime.
+ */
+ boolean mOverlayIsStatic;
+
+ /**
+ * The user-visible SDK version (ex. 26) of the framework against which the application claims
+ * to have been compiled, or {@code 0} if not specified.
+ * <p>
+ * This property is the compile-time equivalent of
+ * {@link android.os.Build.VERSION#SDK_INT Build.VERSION.SDK_INT}.
+ *
+ * @hide For platform use only; we don't expect developers to need to read this value.
+ */
+ public int compileSdkVersion;
+
+ /**
+ * The development codename (ex. "O", "REL") of the framework against which the application
+ * claims to have been compiled, or {@code null} if not specified.
+ * <p>
+ * This property is the compile-time equivalent of
+ * {@link android.os.Build.VERSION#CODENAME Build.VERSION.CODENAME}.
+ *
+ * @hide For platform use only; we don't expect developers to need to read this value.
+ */
+ @Nullable
+ public String compileSdkVersionCodename;
public PackageInfo() {
}
+ /**
+ * Returns true if the package is a valid Runtime Overlay package.
+ * @hide
+ */
+ public boolean isOverlayPackage() {
+ return overlayTarget != null;
+ }
+
+ /**
+ * Returns true if the package is a valid static Runtime Overlay package. Static overlays
+ * are not updatable outside of a system update and are safe to load in the system process.
+ * @hide
+ */
+ public boolean isStaticOverlayPackage() {
+ return overlayTarget != null && mOverlayIsStatic;
+ }
+
@Override
public String toString() {
return "PackageInfo{"
@@ -309,6 +423,7 @@ public class PackageInfo implements Parcelable {
dest.writeString(packageName);
dest.writeStringArray(splitNames);
dest.writeInt(versionCode);
+ dest.writeInt(versionCodeMajor);
dest.writeString(versionName);
dest.writeInt(baseRevisionCode);
dest.writeIntArray(splitRevisionCodes);
@@ -342,8 +457,17 @@ public class PackageInfo implements Parcelable {
dest.writeString(restrictedAccountType);
dest.writeString(requiredAccountType);
dest.writeString(overlayTarget);
- dest.writeInt(isStaticOverlay ? 1 : 0);
+ dest.writeString(overlayCategory);
dest.writeInt(overlayPriority);
+ dest.writeBoolean(mOverlayIsStatic);
+ dest.writeInt(compileSdkVersion);
+ dest.writeString(compileSdkVersionCodename);
+ if (signingInfo != null) {
+ dest.writeInt(1);
+ signingInfo.writeToParcel(dest, parcelableFlags);
+ } else {
+ dest.writeInt(0);
+ }
}
public static final Parcelable.Creator<PackageInfo> CREATOR
@@ -363,6 +487,7 @@ public class PackageInfo implements Parcelable {
packageName = source.readString();
splitNames = source.createStringArray();
versionCode = source.readInt();
+ versionCodeMajor = source.readInt();
versionName = source.readString();
baseRevisionCode = source.readInt();
splitRevisionCodes = source.createIntArray();
@@ -394,8 +519,15 @@ public class PackageInfo implements Parcelable {
restrictedAccountType = source.readString();
requiredAccountType = source.readString();
overlayTarget = source.readString();
- isStaticOverlay = source.readInt() != 0;
+ overlayCategory = source.readString();
overlayPriority = source.readInt();
+ mOverlayIsStatic = source.readBoolean();
+ compileSdkVersion = source.readInt();
+ compileSdkVersionCodename = source.readString();
+ int hasSigningInfo = source.readInt();
+ if (hasSigningInfo != 0) {
+ signingInfo = SigningInfo.CREATOR.createFromParcel(source);
+ }
// The component lists were flattened with the redundant ApplicationInfo
// instances omitted. Distribute the canonical one here as appropriate.
diff --git a/core/java/android/content/pm/PackageInfoLite.java b/core/java/android/content/pm/PackageInfoLite.java
index 1efe082b7fdf..bbf020d76c92 100644
--- a/core/java/android/content/pm/PackageInfoLite.java
+++ b/core/java/android/content/pm/PackageInfoLite.java
@@ -38,9 +38,27 @@ public class PackageInfoLite implements Parcelable {
/**
* The android:versionCode of the package.
+ * @deprecated Use {@link #getLongVersionCode()} instead, which includes both
+ * this and the additional
+ * {@link android.R.styleable#AndroidManifest_versionCode versionCodeMajor} attribute.
*/
+ @Deprecated
public int versionCode;
+ /**
+ * @hide
+ * The android:versionCodeMajor of the package.
+ */
+ public int versionCodeMajor;
+
+ /**
+ * Return {@link #versionCode} and {@link #versionCodeMajor} combined together as a
+ * single long value. The {@link #versionCodeMajor} is placed in the upper 32 bits.
+ */
+ public long getLongVersionCode() {
+ return PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode);
+ }
+
/** Revision code of base APK */
public int baseRevisionCode;
/** Revision codes of any split APKs, ordered by parsed splitName */
@@ -55,10 +73,10 @@ public class PackageInfoLite implements Parcelable {
/**
* Specifies the recommended install location. Can be one of
- * {@link #PackageHelper.RECOMMEND_INSTALL_INTERNAL} to install on internal storage
- * {@link #PackageHelper.RECOMMEND_INSTALL_EXTERNAL} to install on external media
- * {@link PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE} for storage errors
- * {@link PackageHelper.RECOMMEND_FAILED_INVALID_APK} for parse errors.
+ * {@link PackageHelper#RECOMMEND_INSTALL_INTERNAL} to install on internal storage,
+ * {@link PackageHelper#RECOMMEND_INSTALL_EXTERNAL} to install on external media,
+ * {@link PackageHelper#RECOMMEND_FAILED_INSUFFICIENT_STORAGE} for storage errors,
+ * or {@link PackageHelper#RECOMMEND_FAILED_INVALID_APK} for parse errors.
*/
public int recommendedInstallLocation;
public int installLocation;
@@ -82,6 +100,7 @@ public class PackageInfoLite implements Parcelable {
dest.writeString(packageName);
dest.writeStringArray(splitNames);
dest.writeInt(versionCode);
+ dest.writeInt(versionCodeMajor);
dest.writeInt(baseRevisionCode);
dest.writeIntArray(splitRevisionCodes);
dest.writeInt(recommendedInstallLocation);
@@ -111,6 +130,7 @@ public class PackageInfoLite implements Parcelable {
packageName = source.readString();
splitNames = source.createStringArray();
versionCode = source.readInt();
+ versionCodeMajor = source.readInt();
baseRevisionCode = source.readInt();
splitRevisionCodes = source.createIntArray();
recommendedInstallLocation = source.readInt();
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index f4fdcaa44836..25af1a76700f 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -81,6 +81,9 @@ import java.util.List;
* <li>All APKs must have unique split names.
* <li>All installations must contain a single base APK.
* </ul>
+ * <p>
+ * The ApiDemos project contains examples of using this API:
+ * <code>ApiDemos/src/com/example/android/apis/content/InstallApk*.java</code>.
*/
public class PackageInstaller {
private static final String TAG = "PackageInstaller";
@@ -321,7 +324,14 @@ public class PackageInstaller {
*/
public int createSession(@NonNull SessionParams params) throws IOException {
try {
- return mInstaller.createSession(params, mInstallerPackageName, mUserId);
+ final String installerPackage;
+ if (params.installerPackageName == null) {
+ installerPackage = mInstallerPackageName;
+ } else {
+ installerPackage = params.installerPackageName;
+ }
+
+ return mInstaller.createSession(params, installerPackage, mUserId);
} catch (RuntimeException e) {
ExceptionUtils.maybeUnwrapIOException(e);
throw e;
@@ -438,12 +448,21 @@ public class PackageInstaller {
/**
* Uninstall the given package, removing it completely from the device. This
- * method is only available to the current "installer of record" for the
- * package.
+ * method is available to:
+ * <ul>
+ * <li>the current "installer of record" for the package
+ * <li>the device owner
+ * <li>the affiliated profile owner
+ * </ul>
*
* @param packageName The package to uninstall.
* @param statusReceiver Where to deliver the result.
+ *
+ * @see android.app.admin.DevicePolicyManager
*/
+ @RequiresPermission(anyOf = {
+ Manifest.permission.DELETE_PACKAGES,
+ Manifest.permission.REQUEST_DELETE_PACKAGES})
public void uninstall(@NonNull String packageName, @NonNull IntentSender statusReceiver) {
uninstall(packageName, 0 /*flags*/, statusReceiver);
}
@@ -467,15 +486,26 @@ public class PackageInstaller {
/**
* Uninstall the given package with a specific version code, removing it
- * completely from the device. This method is only available to the current
- * "installer of record" for the package. If the version code of the package
+ * completely from the device. If the version code of the package
* does not match the one passed in the versioned package argument this
* method is a no-op. Use {@link PackageManager#VERSION_CODE_HIGHEST} to
* uninstall the latest version of the package.
+ * <p>
+ * This method is available to:
+ * <ul>
+ * <li>the current "installer of record" for the package
+ * <li>the device owner
+ * <li>the affiliated profile owner
+ * </ul>
*
* @param versionedPackage The versioned package to uninstall.
* @param statusReceiver Where to deliver the result.
+ *
+ * @see android.app.admin.DevicePolicyManager
*/
+ @RequiresPermission(anyOf = {
+ Manifest.permission.DELETE_PACKAGES,
+ Manifest.permission.REQUEST_DELETE_PACKAGES})
public void uninstall(@NonNull VersionedPackage versionedPackage,
@NonNull IntentSender statusReceiver) {
uninstall(versionedPackage, 0 /*flags*/, statusReceiver);
@@ -813,7 +843,19 @@ public class PackageInstaller {
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
+ }
+ /** {@hide} */
+ public void write(@NonNull String name, long offsetBytes, long lengthBytes,
+ @NonNull ParcelFileDescriptor fd) throws IOException {
+ try {
+ mSession.write(name, offsetBytes, lengthBytes, fd);
+ } catch (RuntimeException e) {
+ ExceptionUtils.maybeUnwrapIOException(e);
+ throw e;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -913,9 +955,14 @@ public class PackageInstaller {
* Once this method is called, the session is sealed and no additional
* mutations may be performed on the session. If the device reboots
* before the session has been finalized, you may commit the session again.
+ * <p>
+ * If the installer is the device owner or the affiliated profile owner, there will be no
+ * user intervention.
*
* @throws SecurityException if streams opened through
* {@link #openWrite(String, long, long)} are still open.
+ *
+ * @see android.app.admin.DevicePolicyManager
*/
public void commit(@NonNull IntentSender statusReceiver) {
try {
@@ -1072,6 +1119,8 @@ public class PackageInstaller {
public String volumeUuid;
/** {@hide} */
public String[] grantedRuntimePermissions;
+ /** {@hide} */
+ public String installerPackageName;
/**
* Construct parameters for a new package install session.
@@ -1100,6 +1149,7 @@ public class PackageInstaller {
abiOverride = source.readString();
volumeUuid = source.readString();
grantedRuntimePermissions = source.readStringArray();
+ installerPackageName = source.readString();
}
/**
@@ -1184,10 +1234,10 @@ public class PackageInstaller {
}
/**
- * Sets the UID that initiated package installation. This is informational
+ * Sets the UID that initiated the package installation. This is informational
* and may be used as a signal for anti-malware purposes.
*
- * @see PackageManager#EXTRA_VERIFICATION_INSTALLER_UID
+ * @see Intent#EXTRA_ORIGINATING_UID
*/
public void setOriginatingUid(int originatingUid) {
this.originatingUid = originatingUid;
@@ -1295,6 +1345,18 @@ public class PackageInstaller {
}
}
+ /**
+ * Set the installer package for the app.
+ *
+ * By default this is the app that created the {@link PackageInstaller} object.
+ *
+ * @param installerPackageName name of the installer package
+ * {@hide}
+ */
+ public void setInstallerPackageName(String installerPackageName) {
+ this.installerPackageName = installerPackageName;
+ }
+
/** {@hide} */
public void dump(IndentingPrintWriter pw) {
pw.printPair("mode", mode);
@@ -1310,6 +1372,7 @@ public class PackageInstaller {
pw.printPair("abiOverride", abiOverride);
pw.printPair("volumeUuid", volumeUuid);
pw.printPair("grantedRuntimePermissions", grantedRuntimePermissions);
+ pw.printPair("installerPackageName", installerPackageName);
pw.println();
}
@@ -1334,6 +1397,7 @@ public class PackageInstaller {
dest.writeString(abiOverride);
dest.writeString(volumeUuid);
dest.writeStringArray(grantedRuntimePermissions);
+ dest.writeString(installerPackageName);
}
public static final Parcelable.Creator<SessionParams>
diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java
index 11830c294116..52e28a4b4b9b 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -16,10 +16,13 @@
package android.content.pm;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.FloatRange;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.content.res.XmlResourceParser;
-
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Parcel;
@@ -28,7 +31,13 @@ import android.text.Html;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.Printer;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
import java.text.Collator;
+import java.util.BitSet;
import java.util.Comparator;
/**
@@ -41,7 +50,58 @@ import java.util.Comparator;
* in the implementation of Parcelable in subclasses.
*/
public class PackageItemInfo {
+ private static final int LINE_FEED_CODE_POINT = 10;
+ private static final int NBSP_CODE_POINT = 160;
+
+ /**
+ * Flags for {@link #loadSafeLabel(PackageManager, float, int)}
+ *
+ * @hide
+ */
+ @Retention(SOURCE)
+ @IntDef(flag = true, prefix = "SAFE_LABEL_FLAG_",
+ value = {SAFE_LABEL_FLAG_TRIM, SAFE_LABEL_FLAG_SINGLE_LINE,
+ SAFE_LABEL_FLAG_FIRST_LINE})
+ public @interface SafeLabelFlags {}
+
+ /**
+ * Remove {@link Character#isWhitespace(int) whitespace} and non-breaking spaces from the edges
+ * of the label.
+ *
+ * @see #loadSafeLabel(PackageManager, float, int)
+ * @hide
+ */
+ public static final int SAFE_LABEL_FLAG_TRIM = 0x1;
+
+ /**
+ * Force entire string into single line of text (no newlines). Cannot be set at the same time as
+ * {@link #SAFE_LABEL_FLAG_FIRST_LINE}.
+ *
+ * @see #loadSafeLabel(PackageManager, float, int)
+ * @hide
+ */
+ public static final int SAFE_LABEL_FLAG_SINGLE_LINE = 0x2;
+
+ /**
+ * Return only first line of text (truncate at first newline). Cannot be set at the same time as
+ * {@link #SAFE_LABEL_FLAG_SINGLE_LINE}.
+ *
+ * @see #loadSafeLabel(PackageManager, float, int)
+ * @hide
+ */
+ public static final int SAFE_LABEL_FLAG_FIRST_LINE = 0x4;
+
private static final float MAX_LABEL_SIZE_PX = 500f;
+ /** The maximum length of a safe label, in characters */
+ private static final int MAX_SAFE_LABEL_LENGTH = 50000;
+
+ private static volatile boolean sForceSafeLabels = false;
+
+ /** {@hide} */
+ public static void setForceSafeLabels(boolean forceSafeLabels) {
+ sForceSafeLabels = forceSafeLabels;
+ }
+
/**
* Public name of this item. From the "android:name" attribute.
*/
@@ -127,7 +187,16 @@ public class PackageItemInfo {
* @return Returns a CharSequence containing the item's label. If the
* item does not have a label, its name is returned.
*/
- public CharSequence loadLabel(PackageManager pm) {
+ public @NonNull CharSequence loadLabel(@NonNull PackageManager pm) {
+ if (sForceSafeLabels) {
+ return loadSafeLabel(pm);
+ } else {
+ return loadUnsafeLabel(pm);
+ }
+ }
+
+ /** {@hide} */
+ public CharSequence loadUnsafeLabel(PackageManager pm) {
if (nonLocalizedLabel != null) {
return nonLocalizedLabel;
}
@@ -144,32 +213,22 @@ public class PackageItemInfo {
}
/**
- * Same as {@link #loadLabel(PackageManager)} with the addition that
- * the returned label is safe for being presented in the UI since it
- * will not contain new lines and the length will be limited to a
- * reasonable amount. This prevents a malicious party to influence UI
- * layout via the app label misleading the user into performing a
- * detrimental for them action. If the label is too long it will be
- * truncated and ellipsized at the end.
- *
- * @param pm A PackageManager from which the label can be loaded; usually
- * the PackageManager from which you originally retrieved this item
- * @return Returns a CharSequence containing the item's label. If the
- * item does not have a label, its name is returned.
+ * Deprecated use loadSafeLabel(PackageManager, float, int) instead
*
* @hide
*/
@SystemApi
public @NonNull CharSequence loadSafeLabel(@NonNull PackageManager pm) {
// loadLabel() always returns non-null
- String label = loadLabel(pm).toString();
+ String label = loadUnsafeLabel(pm).toString();
// strip HTML tags to avoid <br> and other tags overwriting original message
String labelStr = Html.fromHtml(label).toString();
// If the label contains new line characters it may push the UI
// down to hide a part of it. Labels shouldn't have new line
// characters, so just truncate at the first time one is seen.
- final int labelLength = labelStr.length();
+ final int labelLength = Math.min(labelStr.length(), MAX_SAFE_LABEL_LENGTH);
+ final StringBuffer sb = new StringBuffer(labelLength);
int offset = 0;
while (offset < labelLength) {
final int codePoint = labelStr.codePointAt(offset);
@@ -181,14 +240,19 @@ public class PackageItemInfo {
break;
}
// replace all non-break space to " " in order to be trimmed
+ final int charCount = Character.charCount(codePoint);
if (type == Character.SPACE_SEPARATOR) {
- labelStr = labelStr.substring(0, offset) + " " + labelStr.substring(offset +
- Character.charCount(codePoint));
+ sb.append(' ');
+ } else {
+ sb.append(labelStr.charAt(offset));
+ if (charCount == 2) {
+ sb.append(labelStr.charAt(offset + 1));
+ }
}
- offset += Character.charCount(codePoint);
+ offset += charCount;
}
- labelStr = labelStr.trim();
+ labelStr = sb.toString().trim();
if (labelStr.isEmpty()) {
return packageName;
}
@@ -199,6 +263,216 @@ public class PackageItemInfo {
TextUtils.TruncateAt.END);
}
+ private static boolean isNewline(int codePoint) {
+ int type = Character.getType(codePoint);
+ return type == Character.PARAGRAPH_SEPARATOR || type == Character.LINE_SEPARATOR
+ || codePoint == LINE_FEED_CODE_POINT;
+ }
+
+ private static boolean isWhiteSpace(int codePoint) {
+ return Character.isWhitespace(codePoint) || codePoint == NBSP_CODE_POINT;
+ }
+
+ /**
+ * A special string manipulation class. Just records removals and executes the when onString()
+ * is called.
+ */
+ private static class StringWithRemovedChars {
+ /** The original string */
+ private final String mOriginal;
+
+ /**
+ * One bit per char in string. If bit is set, character needs to be removed. If whole
+ * bit field is not initialized nothing needs to be removed.
+ */
+ private BitSet mRemovedChars;
+
+ StringWithRemovedChars(@NonNull String original) {
+ mOriginal = original;
+ }
+
+ /**
+ * Mark all chars in a range {@code [firstRemoved - firstNonRemoved[} (not including
+ * firstNonRemoved) as removed.
+ */
+ void removeRange(int firstRemoved, int firstNonRemoved) {
+ if (mRemovedChars == null) {
+ mRemovedChars = new BitSet(mOriginal.length());
+ }
+
+ mRemovedChars.set(firstRemoved, firstNonRemoved);
+ }
+
+ /**
+ * Remove all characters before {@code firstNonRemoved}.
+ */
+ void removeAllCharBefore(int firstNonRemoved) {
+ if (mRemovedChars == null) {
+ mRemovedChars = new BitSet(mOriginal.length());
+ }
+
+ mRemovedChars.set(0, firstNonRemoved);
+ }
+
+ /**
+ * Remove all characters after and including {@code firstRemoved}.
+ */
+ void removeAllCharAfter(int firstRemoved) {
+ if (mRemovedChars == null) {
+ mRemovedChars = new BitSet(mOriginal.length());
+ }
+
+ mRemovedChars.set(firstRemoved, mOriginal.length());
+ }
+
+ @Override
+ public String toString() {
+ // Common case, no chars removed
+ if (mRemovedChars == null) {
+ return mOriginal;
+ }
+
+ StringBuilder sb = new StringBuilder(mOriginal.length());
+ for (int i = 0; i < mOriginal.length(); i++) {
+ if (!mRemovedChars.get(i)) {
+ sb.append(mOriginal.charAt(i));
+ }
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Return length or the original string
+ */
+ int length() {
+ return mOriginal.length();
+ }
+
+ /**
+ * Return if a certain {@code offset} of the original string is removed
+ */
+ boolean isRemoved(int offset) {
+ return mRemovedChars != null && mRemovedChars.get(offset);
+ }
+
+ /**
+ * Return codePoint of original string at a certain {@code offset}
+ */
+ int codePointAt(int offset) {
+ return mOriginal.codePointAt(offset);
+ }
+ }
+
+ /**
+ * Load, clean up and truncate label before use.
+ *
+ * <p>This method is meant to remove common mistakes and nefarious formatting from strings that
+ * are used in sensitive parts of the UI.
+ *
+ * <p>This method first treats the string like HTML and then ...
+ * <ul>
+ * <li>Removes new lines or truncates at first new line
+ * <li>Trims the white-space off the end
+ * <li>Truncates the string to a given length
+ * </ul>
+ * ... if specified.
+ *
+ * @param ellipsizeDip Assuming maximum length of the string (in dip), assuming font size 42.
+ * This is roughly 50 characters for {@code ellipsizeDip == 1000}.<br />
+ * Usually ellipsizing should be left to the view showing the string. If a
+ * string is used as an input to another string, it might be useful to
+ * control the length of the input string though. {@code 0} disables this
+ * feature.
+ * @return The safe label
+ * @hide
+ */
+ public @NonNull CharSequence loadSafeLabel(@NonNull PackageManager pm,
+ @FloatRange(from = 0) float ellipsizeDip, @SafeLabelFlags int flags) {
+ boolean onlyKeepFirstLine = ((flags & SAFE_LABEL_FLAG_FIRST_LINE) != 0);
+ boolean forceSingleLine = ((flags & SAFE_LABEL_FLAG_SINGLE_LINE) != 0);
+ boolean trim = ((flags & SAFE_LABEL_FLAG_TRIM) != 0);
+
+ Preconditions.checkNotNull(pm);
+ Preconditions.checkArgument(ellipsizeDip >= 0);
+ Preconditions.checkFlagsArgument(flags, SAFE_LABEL_FLAG_TRIM | SAFE_LABEL_FLAG_SINGLE_LINE
+ | SAFE_LABEL_FLAG_FIRST_LINE);
+ Preconditions.checkArgument(!(onlyKeepFirstLine && forceSingleLine),
+ "Cannot set SAFE_LABEL_FLAG_SINGLE_LINE and SAFE_LABEL_FLAG_FIRST_LINE at the same "
+ + "time");
+
+ // loadLabel() always returns non-null
+ String label = loadUnsafeLabel(pm).toString();
+
+ // Treat string as HTML. This
+ // - converts HTML symbols: e.g. &szlig; -> ß
+ // - applies some HTML tags: e.g. <br> -> \n
+ // - removes invalid characters such as \b
+ // - removes html styling, such as <b>
+ // - applies html formatting: e.g. a<p>b</p>c -> a\n\nb\n\nc
+ // - replaces some html tags by "object replacement" markers: <img> -> \ufffc
+ // - Removes leading white space
+ // - Removes all trailing white space beside a single space
+ // - Collapses double white space
+ StringWithRemovedChars labelStr = new StringWithRemovedChars(
+ Html.fromHtml(label).toString());
+
+ int firstNonWhiteSpace = -1;
+ int firstTrailingWhiteSpace = -1;
+
+ // Remove new lines (if requested) and control characters.
+ int labelLength = labelStr.length();
+ for (int offset = 0; offset < labelLength; ) {
+ int codePoint = labelStr.codePointAt(offset);
+ int type = Character.getType(codePoint);
+ int codePointLen = Character.charCount(codePoint);
+ boolean isNewline = isNewline(codePoint);
+
+ if (offset > MAX_SAFE_LABEL_LENGTH || onlyKeepFirstLine && isNewline) {
+ labelStr.removeAllCharAfter(offset);
+ break;
+ } else if (forceSingleLine && isNewline) {
+ labelStr.removeRange(offset, offset + codePointLen);
+ } else if (type == Character.CONTROL && !isNewline) {
+ labelStr.removeRange(offset, offset + codePointLen);
+ } else if (trim && !isWhiteSpace(codePoint)) {
+ // This is only executed if the code point is not removed
+ if (firstNonWhiteSpace == -1) {
+ firstNonWhiteSpace = offset;
+ }
+ firstTrailingWhiteSpace = offset + codePointLen;
+ }
+
+ offset += codePointLen;
+ }
+
+ if (trim) {
+ // Remove leading and trailing white space
+ if (firstNonWhiteSpace == -1) {
+ // No non whitespace found, remove all
+ labelStr.removeAllCharAfter(0);
+ } else {
+ if (firstNonWhiteSpace > 0) {
+ labelStr.removeAllCharBefore(firstNonWhiteSpace);
+ }
+ if (firstTrailingWhiteSpace < labelLength) {
+ labelStr.removeAllCharAfter(firstTrailingWhiteSpace);
+ }
+ }
+ }
+
+ if (ellipsizeDip == 0) {
+ return labelStr.toString();
+ } else {
+ // Truncate
+ final TextPaint paint = new TextPaint();
+ paint.setTextSize(42);
+
+ return TextUtils.ellipsize(labelStr.toString(), paint, ellipsizeDip,
+ TextUtils.TruncateAt.END);
+ }
+ }
+
/**
* Retrieve the current graphical icon associated with this item. This
* will call back on the given PackageManager to load the icon from
@@ -386,6 +660,24 @@ public class PackageItemInfo {
dest.writeInt(showUserIcon);
}
+ /**
+ * @hide
+ */
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ long token = proto.start(fieldId);
+ if (name != null) {
+ proto.write(PackageItemInfoProto.NAME, name);
+ }
+ proto.write(PackageItemInfoProto.PACKAGE_NAME, packageName);
+ if (labelRes != 0 || nonLocalizedLabel != null || icon != 0 || banner != 0) {
+ proto.write(PackageItemInfoProto.LABEL_RES, labelRes);
+ proto.write(PackageItemInfoProto.NON_LOCALIZED_LABEL, nonLocalizedLabel.toString());
+ proto.write(PackageItemInfoProto.ICON, icon);
+ proto.write(PackageItemInfoProto.BANNER, banner);
+ }
+ proto.end(token);
+ }
+
protected PackageItemInfo(Parcel source) {
name = source.readString();
packageName = source.readString();
diff --git a/core/java/android/content/pm/PackageList.java b/core/java/android/content/pm/PackageList.java
new file mode 100644
index 000000000000..cfd99abc6283
--- /dev/null
+++ b/core/java/android/content/pm/PackageList.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.PackageManagerInternal.PackageListObserver;
+
+import com.android.server.LocalServices;
+
+import java.util.List;
+
+/**
+ * All of the package name installed on the system.
+ * <p>A self observable list that automatically removes the listener when it goes out of scope.
+ *
+ * @hide Only for use within the system server.
+ */
+public class PackageList implements PackageListObserver, AutoCloseable {
+ private final PackageListObserver mWrappedObserver;
+ private final List<String> mPackageNames;
+
+ /**
+ * Create a new object.
+ * <p>Ownership of the given {@link List} transfers to this object and should not
+ * be modified by the caller.
+ */
+ public PackageList(@NonNull List<String> packageNames, @Nullable PackageListObserver observer) {
+ mPackageNames = packageNames;
+ mWrappedObserver = observer;
+ }
+
+ @Override
+ public void onPackageAdded(String packageName) {
+ if (mWrappedObserver != null) {
+ mWrappedObserver.onPackageAdded(packageName);
+ }
+ }
+
+ @Override
+ public void onPackageRemoved(String packageName) {
+ if (mWrappedObserver != null) {
+ mWrappedObserver.onPackageRemoved(packageName);
+ }
+ }
+
+ @Override
+ public void close() throws Exception {
+ LocalServices.getService(PackageManagerInternal.class).removePackageListObserver(this);
+ }
+
+ /**
+ * Returns the names of packages installed on the system.
+ * <p>The list is a copy-in-time and the actual set of installed packages may differ. Real
+ * time updates to the package list are sent via the {@link PackageListObserver} callback.
+ */
+ public @NonNull List<String> getPackageNames() {
+ return mPackageNames;
+ }
+}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 6f98adca72ac..7253e7758008 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -47,11 +47,11 @@ import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.net.Uri;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -68,6 +68,7 @@ import java.io.File;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
+import java.util.Locale;
/**
* Class for retrieving various kinds of information related to the application
@@ -134,6 +135,7 @@ public abstract class PackageManager {
GET_SERVICES,
GET_SHARED_LIBRARY_FILES,
GET_SIGNATURES,
+ GET_SIGNING_CERTIFICATES,
GET_URI_PERMISSION_PATTERNS,
MATCH_UNINSTALLED_PACKAGES,
MATCH_DISABLED_COMPONENTS,
@@ -273,7 +275,10 @@ public abstract class PackageManager {
/**
* {@link PackageInfo} flag: return information about the
* signatures included in the package.
+ *
+ * @deprecated use {@code GET_SIGNING_CERTIFICATES} instead
*/
+ @Deprecated
public static final int GET_SIGNATURES = 0x00000040;
/**
@@ -438,6 +443,7 @@ public abstract class PackageManager {
* @hide
*/
@SystemApi
+ @TestApi
public static final int MATCH_FACTORY_ONLY = 0x00200000;
/**
@@ -453,6 +459,7 @@ public abstract class PackageManager {
* package.
* @hide
*/
+ @TestApi
public static final int MATCH_KNOWN_PACKAGES = MATCH_UNINSTALLED_PACKAGES | MATCH_ANY_USER;
/**
@@ -489,6 +496,14 @@ public abstract class PackageManager {
public static final int MATCH_STATIC_SHARED_LIBRARIES = 0x04000000;
/**
+ * {@link PackageInfo} flag: return the signing certificates associated with
+ * this package. Each entry is a signing certificate that the package
+ * has proven it is authorized to use, usually a past signing certificate from
+ * which it has rotated.
+ */
+ public static final int GET_SIGNING_CERTIFICATES = 0x08000000;
+
+ /**
* Internal flag used to indicate that a system component has done their
* homework and verified that they correctly handle packages and components
* that come and go over time. In particular:
@@ -788,7 +803,8 @@ public abstract class PackageManager {
/**
* Flag parameter for {@link #installPackage} to indicate that this package is an
- * upgrade to a package that refers to the SDK via release letter.
+ * upgrade to a package that refers to the SDK via release letter or is targeting an SDK via
+ * release letter that the current build does not support.
*
* @hide
*/
@@ -872,8 +888,8 @@ public abstract class PackageManager {
public static final int INSTALL_REASON_USER = 4;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} on success.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * on success.
*
* @hide
*/
@@ -881,8 +897,8 @@ public abstract class PackageManager {
public static final int INSTALL_SUCCEEDED = 1;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the package is already installed.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the package is already installed.
*
* @hide
*/
@@ -890,8 +906,8 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_ALREADY_EXISTS = -1;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the package archive file is invalid.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the package archive file is invalid.
*
* @hide
*/
@@ -899,8 +915,8 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_INVALID_APK = -2;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the URI passed in is invalid.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the URI passed in is invalid.
*
* @hide
*/
@@ -908,9 +924,9 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_INVALID_URI = -3;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the package manager service found that
- * the device didn't have enough storage space to install the app.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the package manager service found that the device didn't have enough storage space to
+ * install the app.
*
* @hide
*/
@@ -918,9 +934,8 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_INSUFFICIENT_STORAGE = -4;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if a package is already installed with
- * the same name.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if a package is already installed with the same name.
*
* @hide
*/
@@ -928,9 +943,8 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_DUPLICATE_PACKAGE = -5;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the requested shared user does not
- * exist.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the requested shared user does not exist.
*
* @hide
*/
@@ -938,10 +952,9 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_NO_SHARED_USER = -6;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if a previously installed package of the
- * same name has a different signature than the new package (and the old
- * package's data was not removed).
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if a previously installed package of the same name has a different signature than the new
+ * package (and the old package's data was not removed).
*
* @hide
*/
@@ -949,10 +962,9 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_UPDATE_INCOMPATIBLE = -7;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package is requested a shared
- * user which is already installed on the device and does not have matching
- * signature.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package is requested a shared user which is already installed on the device and
+ * does not have matching signature.
*
* @hide
*/
@@ -960,9 +972,8 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_SHARED_USER_INCOMPATIBLE = -8;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package uses a shared library
- * that is not available.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package uses a shared library that is not available.
*
* @hide
*/
@@ -970,9 +981,8 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_MISSING_SHARED_LIBRARY = -9;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package uses a shared library
- * that is not available.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package uses a shared library that is not available.
*
* @hide
*/
@@ -980,10 +990,9 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_REPLACE_COULDNT_DELETE = -10;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package failed while
- * optimizing and validating its dex files, either because there was not
- * enough storage or the validation failed.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package failed while optimizing and validating its dex files, either because there
+ * was not enough storage or the validation failed.
*
* @hide
*/
@@ -991,9 +1000,9 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_DEXOPT = -11;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package failed because the
- * current SDK version is older than that required by the package.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package failed because the current SDK version is older than that required by the
+ * package.
*
* @hide
*/
@@ -1001,10 +1010,9 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_OLDER_SDK = -12;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package failed because it
- * contains a content provider with the same authority as a provider already
- * installed in the system.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package failed because it contains a content provider with the same authority as a
+ * provider already installed in the system.
*
* @hide
*/
@@ -1012,9 +1020,9 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_CONFLICTING_PROVIDER = -13;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package failed because the
- * current SDK version is newer than that required by the package.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package failed because the current SDK version is newer than that required by the
+ * package.
*
* @hide
*/
@@ -1022,10 +1030,9 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_NEWER_SDK = -14;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package failed because it has
- * specified that it is a test-only package and the caller has not supplied
- * the {@link #INSTALL_ALLOW_TEST} flag.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package failed because it has specified that it is a test-only package and the
+ * caller has not supplied the {@link #INSTALL_ALLOW_TEST} flag.
*
* @hide
*/
@@ -1033,9 +1040,9 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_TEST_ONLY = -15;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the package being installed contains
- * native code, but none that is compatible with the device's CPU_ABI.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the package being installed contains native code, but none that is compatible with the
+ * device's CPU_ABI.
*
* @hide
*/
@@ -1043,9 +1050,8 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_CPU_ABI_INCOMPATIBLE = -16;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package uses a feature that is
- * not available.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package uses a feature that is not available.
*
* @hide
*/
@@ -1054,9 +1060,9 @@ public abstract class PackageManager {
// ------ Errors related to sdcard
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if a secure container mount point
- * couldn't be accessed on external media.
+ * Installation return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if a secure container mount point couldn't be
+ * accessed on external media.
*
* @hide
*/
@@ -1064,9 +1070,8 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_CONTAINER_ERROR = -18;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package couldn't be installed
- * in the specified install location.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package couldn't be installed in the specified install location.
*
* @hide
*/
@@ -1074,9 +1079,9 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_INVALID_INSTALL_LOCATION = -19;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package couldn't be installed
- * in the specified install location because the media is not available.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package couldn't be installed in the specified install location because the media
+ * is not available.
*
* @hide
*/
@@ -1084,9 +1089,8 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_MEDIA_UNAVAILABLE = -20;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package couldn't be installed
- * because the verification timed out.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package couldn't be installed because the verification timed out.
*
* @hide
*/
@@ -1094,9 +1098,8 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_VERIFICATION_TIMEOUT = -21;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package couldn't be installed
- * because the verification did not succeed.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package couldn't be installed because the verification did not succeed.
*
* @hide
*/
@@ -1104,9 +1107,8 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_VERIFICATION_FAILURE = -22;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the package changed from what the
- * calling program expected.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the package changed from what the calling program expected.
*
* @hide
*/
@@ -1114,28 +1116,25 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_PACKAGE_CHANGED = -23;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package is assigned a
- * different UID than it previously held.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package is assigned a different UID than it previously held.
*
* @hide
*/
public static final int INSTALL_FAILED_UID_CHANGED = -24;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package has an older version
- * code than the currently installed package.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package has an older version code than the currently installed package.
*
* @hide
*/
public static final int INSTALL_FAILED_VERSION_DOWNGRADE = -25;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the old package has target SDK high
- * enough to support runtime permission and the new package has target SDK
- * low enough to not support runtime permissions.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the old package has target SDK high enough to support runtime permission and the new
+ * package has target SDK low enough to not support runtime permissions.
*
* @hide
*/
@@ -1143,9 +1142,8 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE = -26;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package attempts to downgrade the
- * target sandbox version of the app.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package attempts to downgrade the target sandbox version of the app.
*
* @hide
*/
@@ -1153,9 +1151,9 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_SANDBOX_VERSION_DOWNGRADE = -27;
/**
- * Installation parse return code: this is passed to the
- * {@link IPackageInstallObserver} if the parser was given a path that is
- * not a file, or does not end with the expected '.apk' extension.
+ * Installation parse return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser was given a path that is not a
+ * file, or does not end with the expected '.apk' extension.
*
* @hide
*/
@@ -1163,8 +1161,8 @@ public abstract class PackageManager {
public static final int INSTALL_PARSE_FAILED_NOT_APK = -100;
/**
- * Installation parse return code: this is passed to the
- * {@link IPackageInstallObserver} if the parser was unable to retrieve the
+ * Installation parse return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser was unable to retrieve the
* AndroidManifest.xml file.
*
* @hide
@@ -1173,8 +1171,8 @@ public abstract class PackageManager {
public static final int INSTALL_PARSE_FAILED_BAD_MANIFEST = -101;
/**
- * Installation parse return code: this is passed to the
- * {@link IPackageInstallObserver} if the parser encountered an unexpected
+ * Installation parse return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser encountered an unexpected
* exception.
*
* @hide
@@ -1183,9 +1181,9 @@ public abstract class PackageManager {
public static final int INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION = -102;
/**
- * Installation parse return code: this is passed to the
- * {@link IPackageInstallObserver} if the parser did not find any
- * certificates in the .apk.
+ * Installation parse return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser did not find any certificates in
+ * the .apk.
*
* @hide
*/
@@ -1193,9 +1191,9 @@ public abstract class PackageManager {
public static final int INSTALL_PARSE_FAILED_NO_CERTIFICATES = -103;
/**
- * Installation parse return code: this is passed to the
- * {@link IPackageInstallObserver} if the parser found inconsistent
- * certificates on the files in the .apk.
+ * Installation parse return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser found inconsistent certificates on
+ * the files in the .apk.
*
* @hide
*/
@@ -1203,8 +1201,8 @@ public abstract class PackageManager {
public static final int INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES = -104;
/**
- * Installation parse return code: this is passed to the
- * {@link IPackageInstallObserver} if the parser encountered a
+ * Installation parse return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser encountered a
* CertificateEncodingException in one of the files in the .apk.
*
* @hide
@@ -1213,9 +1211,9 @@ public abstract class PackageManager {
public static final int INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING = -105;
/**
- * Installation parse return code: this is passed to the
- * {@link IPackageInstallObserver} if the parser encountered a bad or
- * missing package name in the manifest.
+ * Installation parse return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser encountered a bad or missing
+ * package name in the manifest.
*
* @hide
*/
@@ -1223,9 +1221,9 @@ public abstract class PackageManager {
public static final int INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME = -106;
/**
- * Installation parse return code: this is passed to the
- * {@link IPackageInstallObserver} if the parser encountered a bad shared
- * user id name in the manifest.
+ * Installation parse return code: tthis is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser encountered a bad shared user id
+ * name in the manifest.
*
* @hide
*/
@@ -1233,8 +1231,8 @@ public abstract class PackageManager {
public static final int INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID = -107;
/**
- * Installation parse return code: this is passed to the
- * {@link IPackageInstallObserver} if the parser encountered some structural
+ * Installation parse return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser encountered some structural
* problem in the manifest.
*
* @hide
@@ -1243,9 +1241,9 @@ public abstract class PackageManager {
public static final int INSTALL_PARSE_FAILED_MANIFEST_MALFORMED = -108;
/**
- * Installation parse return code: this is passed to the
- * {@link IPackageInstallObserver} if the parser did not find any actionable
- * tags (instrumentation or application) in the manifest.
+ * Installation parse return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser did not find any actionable tags
+ * (instrumentation or application) in the manifest.
*
* @hide
*/
@@ -1253,9 +1251,9 @@ public abstract class PackageManager {
public static final int INSTALL_PARSE_FAILED_MANIFEST_EMPTY = -109;
/**
- * Installation failed return code: this is passed to the
- * {@link IPackageInstallObserver} if the system failed to install the
- * package because of system issues.
+ * Installation failed return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the system failed to install the package
+ * because of system issues.
*
* @hide
*/
@@ -1263,24 +1261,23 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_INTERNAL_ERROR = -110;
/**
- * Installation failed return code: this is passed to the
- * {@link IPackageInstallObserver} if the system failed to install the
- * package because the user is restricted from installing apps.
+ * Installation failed return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the system failed to install the package
+ * because the user is restricted from installing apps.
*
* @hide
*/
public static final int INSTALL_FAILED_USER_RESTRICTED = -111;
/**
- * Installation failed return code: this is passed to the
- * {@link IPackageInstallObserver} if the system failed to install the
- * package because it is attempting to define a permission that is already
- * defined by some existing package.
+ * Installation failed return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the system failed to install the package
+ * because it is attempting to define a permission that is already defined by some existing
+ * package.
* <p>
- * The package name of the app which has already defined the permission is
- * passed to a {@link PackageInstallObserver}, if any, as the
- * {@link #EXTRA_FAILURE_EXISTING_PACKAGE} string extra; and the name of the
- * permission being redefined is passed in the
+ * The package name of the app which has already defined the permission is passed to a
+ * {@link PackageInstallObserver}, if any, as the {@link #EXTRA_FAILURE_EXISTING_PACKAGE} string
+ * extra; and the name of the permission being redefined is passed in the
* {@link #EXTRA_FAILURE_EXISTING_PERMISSION} string extra.
*
* @hide
@@ -1288,10 +1285,9 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_DUPLICATE_PERMISSION = -112;
/**
- * Installation failed return code: this is passed to the
- * {@link IPackageInstallObserver} if the system failed to install the
- * package because its packaged native code did not match any of the ABIs
- * supported by the system.
+ * Installation failed return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the system failed to install the package
+ * because its packaged native code did not match any of the ABIs supported by the system.
*
* @hide
*/
@@ -1332,6 +1328,7 @@ public abstract class PackageManager {
DELETE_ALL_USERS,
DELETE_SYSTEM_APP,
DELETE_DONT_KILL_APP,
+ DELETE_CHATTY,
})
@Retention(RetentionPolicy.SOURCE)
public @interface DeleteFlags {}
@@ -1373,6 +1370,14 @@ public abstract class PackageManager {
public static final int DELETE_DONT_KILL_APP = 0x00000008;
/**
+ * Flag parameter for {@link #deletePackage} to indicate that package deletion
+ * should be chatty.
+ *
+ * @hide
+ */
+ public static final int DELETE_CHATTY = 0x80000000;
+
+ /**
* Return code for when package deletion succeeds. This is passed to the
* {@link IPackageDeleteObserver} if the system succeeded in deleting the
* package.
@@ -1652,7 +1657,8 @@ public abstract class PackageManager {
/**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device includes at least one form of audio
- * output, such as speakers, audio jack or streaming over bluetooth
+ * output, as defined in the Android Compatibility Definition Document (CDD)
+ * <a href="https://source.android.com/compatibility/android-cdd#7_8_audio">section 7.8 Audio</a>.
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_AUDIO_OUTPUT = "android.hardware.audio.output";
@@ -1765,6 +1771,16 @@ public abstract class PackageManager {
"android.hardware.camera.capability.raw";
/**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: At least one
+ * of the cameras on the device supports the
+ * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING
+ * MOTION_TRACKING} capability level.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_CAMERA_AR =
+ "android.hardware.camera.ar";
+
+ /**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device is capable of communicating with
* consumer IR devices.
@@ -1939,6 +1955,12 @@ public abstract class PackageManager {
* <li>Minor version number in bits 21-12</li>
* <li>Patch version number in bits 11-0</li>
* </ul>
+ * A version of 1.1.0 or higher also indicates:
+ * <ul>
+ * <li>{@code SYNC_FD} external semaphore and fence handles are supported.</li>
+ * <li>{@code VkPhysicalDeviceSamplerYcbcrConversionFeatures::samplerYcbcrConversion} is
+ * supported.</li>
+ * </ul>
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_VULKAN_HARDWARE_VERSION = "android.hardware.vulkan.version";
@@ -2050,6 +2072,15 @@ public abstract class PackageManager {
"android.hardware.sensor.hifi_sensors";
/**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+ * The device supports a hardware mechanism for invoking an assist gesture.
+ * @see android.provider.Settings.Secure#ASSIST_GESTURE_ENABLED
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_ASSIST_GESTURE = "android.hardware.sensor.assist";
+
+ /**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device has a telephony radio with data
* communication support.
@@ -2250,6 +2281,13 @@ public abstract class PackageManager {
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_APP_WIDGETS = "android.software.app_widgets";
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device supports the
+ * {@link android.R.attr#cantSaveState} API.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_CANT_SAVE_STATE = "android.software.cant_save_state";
/**
* @hide
@@ -2345,6 +2383,14 @@ public abstract class PackageManager {
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device supports Wi-Fi RTT (IEEE 802.11mc).
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_WIFI_RTT = "android.hardware.wifi.rtt";
+
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device supports LoWPAN networking.
* @hide
*/
@@ -2488,10 +2534,17 @@ public abstract class PackageManager {
= "android.software.securely_removes_users";
/** {@hide} */
+ @TestApi
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_FILE_BASED_ENCRYPTION
= "android.software.file_based_encryption";
+ /** {@hide} */
+ @TestApi
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_ADOPTABLE_STORAGE
+ = "android.software.adoptable_storage";
+
/**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
* The device has a full implementation of the android.webkit.* APIs. Devices
@@ -2538,31 +2591,22 @@ public abstract class PackageManager {
* Devices declaring this feature must include an application implementing a
* {@link android.service.vr.VrListenerService} that can be targeted by VR applications via
* {@link android.app.Activity#setVrModeEnabled}.
+ * @deprecated use {@link #FEATURE_VR_MODE_HIGH_PERFORMANCE} instead.
*/
+ @Deprecated
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_VR_MODE = "android.software.vr.mode";
/**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
- * The device implements {@link #FEATURE_VR_MODE} but additionally meets extra CDD requirements
- * to provide a high-quality VR experience. In general, devices declaring this feature will
- * additionally:
- * <ul>
- * <li>Deliver consistent performance at a high framerate over an extended period of time
- * for typical VR application CPU/GPU workloads with a minimal number of frame drops for VR
- * applications that have called
- * {@link android.view.Window#setSustainedPerformanceMode}.</li>
- * <li>Implement {@link #FEATURE_HIFI_SENSORS} and have a low sensor latency.</li>
- * <li>Include optimizations to lower display persistence while running VR applications.</li>
- * <li>Implement an optimized render path to minimize latency to draw to the device's main
- * display.</li>
- * <li>Include the following EGL extensions: EGL_ANDROID_create_native_client_buffer,
- * EGL_ANDROID_front_buffer_auto_refresh, EGL_EXT_protected_content,
- * EGL_KHR_mutable_render_buffer, EGL_KHR_reusable_sync, and EGL_KHR_wait_sync.</li>
- * <li>Provide at least one CPU core that is reserved for use solely by the top, foreground
- * VR application process for critical render threads while such an application is
- * running.</li>
- * </ul>
+ * The device implements an optimized mode for virtual reality (VR) applications that handles
+ * stereoscopic rendering of notifications, disables most monocular system UI components
+ * while a VR application has user focus and meets extra CDD requirements to provide a
+ * high-quality VR experience.
+ * Devices declaring this feature must include an application implementing a
+ * {@link android.service.vr.VrListenerService} that can be targeted by VR applications via
+ * {@link android.app.Activity#setVrModeEnabled}.
+ * and must meet CDD requirements to provide a high-quality VR experience.
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_VR_MODE_HIGH_PERFORMANCE
@@ -2585,6 +2629,25 @@ public abstract class PackageManager {
public static final String FEATURE_VR_HEADTRACKING = "android.hardware.vr.headtracking";
/**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+ * The device has a StrongBox hardware-backed Keystore.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_STRONGBOX_KEYSTORE =
+ "android.hardware.strongbox_keystore";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+ * The device has a Keymaster implementation that supports Device ID attestation.
+ *
+ * @see DevicePolicyManager#isDeviceIdAttestationSupported
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_DEVICE_ID_ATTESTATION =
+ "android.software.device_id_attestation";
+
+ /**
* Action to external storage service to clean out removed apps.
* @hide
*/
@@ -2650,13 +2713,22 @@ public abstract class PackageManager {
/**
* Extra field name for the version code of a package pending verification.
- *
+ * @deprecated Use {@link #EXTRA_VERIFICATION_LONG_VERSION_CODE} instead.
* @hide
*/
+ @Deprecated
public static final String EXTRA_VERIFICATION_VERSION_CODE
= "android.content.pm.extra.VERIFICATION_VERSION_CODE";
/**
+ * Extra field name for the long version code of a package pending verification.
+ *
+ * @hide
+ */
+ public static final String EXTRA_VERIFICATION_LONG_VERSION_CODE =
+ "android.content.pm.extra.VERIFICATION_LONG_VERSION_CODE";
+
+ /**
* Extra field name for the ID of a intent filter pending verification.
* Passed to an intent filter verifier and is used to call back to
* {@link #verifyIntentFilter}
@@ -2921,6 +2993,11 @@ public abstract class PackageManager {
*/
public static final int VERSION_CODE_HIGHEST = -1;
+ /** {@hide} */
+ public int getUserId() {
+ return UserHandle.myUserId();
+ }
+
/**
* Retrieve overall information about an application package that is
* installed on the system.
@@ -3038,6 +3115,21 @@ public abstract class PackageManager {
public abstract @Nullable Intent getLeanbackLaunchIntentForPackage(@NonNull String packageName);
/**
+ * Return a "good" intent to launch a front-door Car activity in a
+ * package, for use for example to implement an "open" button when browsing
+ * through packages. The current implementation will look for a main
+ * activity in the category {@link Intent#CATEGORY_CAR_LAUNCHER}, or
+ * return null if no main car activities are found.
+ *
+ * @param packageName The name of the package to inspect.
+ * @return Returns either a fully-qualified Intent that can be used to launch
+ * the main Car activity in the package, or null if the package
+ * does not contain such an activity.
+ * @hide
+ */
+ public abstract @Nullable Intent getCarLaunchIntentForPackage(@NonNull String packageName);
+
+ /**
* Return an array of all of the POSIX secondary group IDs that have been
* assigned to the given package.
* <p>
@@ -3271,7 +3363,7 @@ public abstract class PackageManager {
@ComponentInfoFlags int flags) throws NameNotFoundException;
/**
- * Return a List of all packages that are installed on the device.
+ * Return a List of all packages that are installed for the current user.
*
* @param flags Additional option flags to modify the data returned.
* @return A List of PackageInfo objects, one for each installed package,
@@ -3319,6 +3411,7 @@ public abstract class PackageManager {
* deleted with {@code DONT_DELETE_DATA} flag set).
* @hide
*/
+ @TestApi
@SystemApi
@RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
public abstract List<PackageInfo> getInstalledPackagesAsUser(@PackageInfoFlags int flags,
@@ -3638,6 +3731,7 @@ public abstract class PackageManager {
*
* @hide
*/
+ @TestApi
public abstract @Nullable String[] getNamesForUids(int[] uids);
/**
@@ -3657,8 +3751,8 @@ public abstract class PackageManager {
throws NameNotFoundException;
/**
- * Return a List of all application packages that are installed on the
- * device. If flag GET_UNINSTALLED_PACKAGES has been set, a list of all
+ * Return a List of all application packages that are installed for the
+ * current user. If flag GET_UNINSTALLED_PACKAGES has been set, a list of all
* applications including those deleted with {@code DONT_DELETE_DATA}
* (partially installed apps with data directory) will be returned.
*
@@ -3694,6 +3788,7 @@ public abstract class PackageManager {
* deleted with {@code DONT_DELETE_DATA} flag set).
* @hide
*/
+ @TestApi
public abstract List<ApplicationInfo> getInstalledApplicationsAsUser(
@ApplicationInfoFlags int flags, @UserIdInt int userId);
@@ -3760,7 +3855,7 @@ public abstract class PackageManager {
public abstract int getInstantAppCookieMaxBytes();
/**
- * @deprecated
+ * deprecated
* @hide
*/
public abstract int getInstantAppCookieMaxSize();
@@ -3867,6 +3962,7 @@ public abstract class PackageManager {
*
* @hide
*/
+ @TestApi
public abstract @NonNull String getServicesSystemSharedLibraryPackageName();
/**
@@ -3876,6 +3972,7 @@ public abstract class PackageManager {
*
* @hide
*/
+ @TestApi
public abstract @NonNull String getSharedSystemSharedLibraryPackageName();
/**
@@ -4111,6 +4208,12 @@ public abstract class PackageManager {
public abstract ResolveInfo resolveService(Intent intent, @ResolveInfoFlags int flags);
/**
+ * @hide
+ */
+ public abstract ResolveInfo resolveServiceAsUser(Intent intent, @ResolveInfoFlags int flags,
+ @UserIdInt int userId);
+
+ /**
* Retrieve all services that can match the given intent.
*
* @param intent The desired intent as per resolveService().
@@ -4707,7 +4810,7 @@ public abstract class PackageManager {
PackageParser.Package pkg = parser.parseMonolithicPackage(apkFile, 0);
if ((flags & GET_SIGNATURES) != 0) {
- PackageParser.collectCertificates(pkg, 0);
+ PackageParser.collectCertificates(pkg, false /* skipVerify */);
}
PackageUserState state = new PackageUserState();
return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, state);
@@ -4717,27 +4820,6 @@ public abstract class PackageManager {
}
/**
- * @deprecated replaced by {@link PackageInstaller}
- * @hide
- */
- @Deprecated
- public abstract void installPackage(
- Uri packageURI,
- IPackageInstallObserver observer,
- @InstallFlags int flags,
- String installerPackageName);
- /**
- * @deprecated replaced by {@link PackageInstaller}
- * @hide
- */
- @Deprecated
- public abstract void installPackage(
- Uri packageURI,
- PackageInstallObserver observer,
- @InstallFlags int flags,
- String installerPackageName);
-
- /**
* If there is already an application with the given package name installed
* on the system for other users, also install it for the calling user.
* @hide
@@ -5023,6 +5105,7 @@ public abstract class PackageManager {
* which market the package came from.
*
* @param packageName The name of the package to query
+ * @throws IllegalArgumentException if the given package name is not installed
*/
public abstract String getInstallerPackageName(String packageName);
@@ -5174,7 +5257,7 @@ public abstract class PackageManager {
*/
@Deprecated
public void getPackageSizeInfo(String packageName, IPackageStatsObserver observer) {
- getPackageSizeInfoAsUser(packageName, UserHandle.myUserId(), observer);
+ getPackageSizeInfoAsUser(packageName, getUserId(), observer);
}
/**
@@ -5452,37 +5535,130 @@ public abstract class PackageManager {
/**
* Puts the package in a suspended state, where attempts at starting activities are denied.
*
- * <p>It doesn't remove the data or the actual package file. The application notifications
- * will be hidden, the application will not show up in recents, will not be able to show
- * toasts or dialogs or ring the device.
+ * <p>It doesn't remove the data or the actual package file. The application's notifications
+ * will be hidden, any of its started activities will be stopped and it will not be able to
+ * show toasts or system alert windows or ring the device.
+ *
+ * <p>When the user tries to launch a suspended app, a system dialog with the given
+ * {@code dialogMessage} will be shown instead. Since the message is supplied to the system as
+ * a {@link String}, the caller needs to take care of localization as needed.
+ * The dialog message can optionally contain a placeholder for the name of the suspended app.
+ * The system uses {@link String#format(Locale, String, Object...) String.format} to insert the
+ * app name into the message, so an example format string could be {@code "The app %1$s is
+ * currently suspended"}. This makes it easier for callers to provide a single message which
+ * works for all the packages being suspended in a single call.
*
* <p>The package must already be installed. If the package is uninstalled while suspended
- * the package will no longer be suspended.
+ * the package will no longer be suspended. </p>
+ *
+ * <p>Optionally, the suspending app can provide extra information in the form of
+ * {@link PersistableBundle} objects to be shared with the apps being suspended and the
+ * launcher to support customization that they might need to handle the suspended state.
+ *
+ * <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} or
+ * {@link Manifest.permission#MANAGE_USERS} to use this api.</p>
*
* @param packageNames The names of the packages to set the suspended status.
* @param suspended If set to {@code true} than the packages will be suspended, if set to
- * {@code false} the packages will be unsuspended.
- * @param userId The user id.
+ * {@code false}, the packages will be unsuspended.
+ * @param appExtras An optional {@link PersistableBundle} that the suspending app can provide
+ * which will be shared with the apps being suspended. Ignored if
+ * {@code suspended} is false.
+ * @param launcherExtras An optional {@link PersistableBundle} that the suspending app can
+ * provide which will be shared with the launcher. Ignored if
+ * {@code suspended} is false.
+ * @param dialogMessage The message to be displayed to the user, when they try to launch a
+ * suspended app.
*
- * @return an array of package names for which the suspended status is not set as requested in
- * this method.
+ * @return an array of package names for which the suspended status could not be set as
+ * requested in this method.
*
* @hide
*/
- public abstract String[] setPackagesSuspendedAsUser(
- String[] packageNames, boolean suspended, @UserIdInt int userId);
+ @SystemApi
+ @RequiresPermission(Manifest.permission.SUSPEND_APPS)
+ public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
+ @Nullable PersistableBundle appExtras, @Nullable PersistableBundle launcherExtras,
+ String dialogMessage) {
+ throw new UnsupportedOperationException("setPackagesSuspended not implemented");
+ }
/**
- * @see #setPackageSuspendedAsUser(String, boolean, int)
+ * @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
* @param packageName The name of the package to get the suspended status of.
* @param userId The user id.
* @return {@code true} if the package is suspended or {@code false} if the package is not
- * suspended or could not be found.
+ * suspended.
+ * @throws IllegalArgumentException if the package was not found.
* @hide
*/
public abstract boolean isPackageSuspendedForUser(String packageName, int userId);
/**
+ * Query if an app is currently suspended.
+ *
+ * @return {@code true} if the given package is suspended, {@code false} otherwise
+ * @throws NameNotFoundException if the package could not be found.
+ *
+ * @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
+ * @hide
+ */
+ @SystemApi
+ public boolean isPackageSuspended(String packageName) throws NameNotFoundException {
+ throw new UnsupportedOperationException("isPackageSuspended not implemented");
+ }
+
+ /**
+ * Apps can query this to know if they have been suspended. A system app with the permission
+ * {@code android.permission.SUSPEND_APPS} can put any app on the device into a suspended state.
+ *
+ * <p>While in this state, the application's notifications will be hidden, any of its started
+ * activities will be stopped and it will not be able to show toasts or dialogs or ring the
+ * device. When the user tries to launch a suspended app, the system will, instead, show a
+ * dialog to the user informing them that they cannot use this app while it is suspended.
+ *
+ * <p>When an app is put into this state, the broadcast action
+ * {@link Intent#ACTION_MY_PACKAGE_SUSPENDED} will be delivered to any of its broadcast
+ * receivers that included this action in their intent-filters, <em>including manifest
+ * receivers.</em> Similarly, a broadcast action {@link Intent#ACTION_MY_PACKAGE_UNSUSPENDED}
+ * is delivered when a previously suspended app is taken out of this state.
+ * </p>
+ *
+ * @return {@code true} if the calling package has been suspended, {@code false} otherwise.
+ *
+ * @see #getSuspendedPackageAppExtras()
+ * @see Intent#ACTION_MY_PACKAGE_SUSPENDED
+ * @see Intent#ACTION_MY_PACKAGE_UNSUSPENDED
+ */
+ public boolean isPackageSuspended() {
+ throw new UnsupportedOperationException("isPackageSuspended not implemented");
+ }
+
+ /**
+ * Returns a {@link Bundle} of extras that was meant to be sent to the calling app when it was
+ * suspended. An app with the permission {@code android.permission.SUSPEND_APPS} can supply this
+ * to the system at the time of suspending an app.
+ *
+ * <p>This is the same {@link Bundle} that is sent along with the broadcast
+ * {@link Intent#ACTION_MY_PACKAGE_SUSPENDED}, whenever the app is suspended. The contents of
+ * this {@link Bundle} are a contract between the suspended app and the suspending app.
+ *
+ * <p>Note: These extras are optional, so if no extras were supplied to the system, this method
+ * will return {@code null}, even when the calling app has been suspended.
+ *
+ * @return A {@link Bundle} containing the extras for the app, or {@code null} if the
+ * package is not currently suspended.
+ *
+ * @see #isPackageSuspended()
+ * @see Intent#ACTION_MY_PACKAGE_UNSUSPENDED
+ * @see Intent#ACTION_MY_PACKAGE_SUSPENDED
+ * @see Intent#EXTRA_SUSPENDED_PACKAGE_EXTRAS
+ */
+ public @Nullable Bundle getSuspendedPackageAppExtras() {
+ throw new UnsupportedOperationException("getSuspendedPackageAppExtras not implemented");
+ }
+
+ /**
* Provide a hint of what the {@link ApplicationInfo#category} value should
* be for the given package.
* <p>
@@ -5751,25 +5927,6 @@ public abstract class PackageManager {
}
/** {@hide} */
- public static class LegacyPackageInstallObserver extends PackageInstallObserver {
- private final IPackageInstallObserver mLegacy;
-
- public LegacyPackageInstallObserver(IPackageInstallObserver legacy) {
- mLegacy = legacy;
- }
-
- @Override
- public void onPackageInstalled(String basePackageName, int returnCode, String msg,
- Bundle extras) {
- if (mLegacy == null) return;
- try {
- mLegacy.packageInstalled(basePackageName, returnCode);
- } catch (RemoteException ignored) {
- }
- }
- }
-
- /** {@hide} */
public static class LegacyPackageDeleteObserver extends PackageDeleteObserver {
private final IPackageDeleteObserver mLegacy;
@@ -5900,4 +6057,123 @@ public abstract class PackageManager {
public @NonNull ArtManager getArtManager() {
throw new UnsupportedOperationException("getArtManager not implemented in subclass");
}
+
+ /**
+ * Sets or clears the harmful app warning details for the given app.
+ *
+ * When set, any attempt to launch an activity in this package will be intercepted and a
+ * warning dialog will be shown to the user instead, with the given warning. The user
+ * will have the option to proceed with the activity launch, or to uninstall the application.
+ *
+ * @param packageName The full name of the package to warn on.
+ * @param warning A warning string to display to the user describing the threat posed by the
+ * application, or null to clear the warning.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.SET_HARMFUL_APP_WARNINGS)
+ @SystemApi
+ public void setHarmfulAppWarning(@NonNull String packageName, @Nullable CharSequence warning) {
+ throw new UnsupportedOperationException("setHarmfulAppWarning not implemented in subclass");
+ }
+
+ /**
+ * Returns the harmful app warning string for the given app, or null if there is none set.
+ *
+ * @param packageName The full name of the desired package.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.SET_HARMFUL_APP_WARNINGS)
+ @Nullable
+ @SystemApi
+ public CharSequence getHarmfulAppWarning(@NonNull String packageName) {
+ throw new UnsupportedOperationException("getHarmfulAppWarning not implemented in subclass");
+ }
+
+ /** @hide */
+ @IntDef(prefix = { "CERT_INPUT_" }, value = {
+ CERT_INPUT_RAW_X509,
+ CERT_INPUT_SHA256
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CertificateInputType {}
+
+ /**
+ * Certificate input bytes: the input bytes represent an encoded X.509 Certificate which could
+ * be generated using an {@code CertificateFactory}
+ */
+ public static final int CERT_INPUT_RAW_X509 = 0;
+
+ /**
+ * Certificate input bytes: the input bytes represent the SHA256 output of an encoded X.509
+ * Certificate.
+ */
+ public static final int CERT_INPUT_SHA256 = 1;
+
+ /**
+ * Searches the set of signing certificates by which the given package has proven to have been
+ * signed. This should be used instead of {@code getPackageInfo} with {@code GET_SIGNATURES}
+ * since it takes into account the possibility of signing certificate rotation, except in the
+ * case of packages that are signed by multiple certificates, for which signing certificate
+ * rotation is not supported. This method is analogous to using {@code getPackageInfo} with
+ * {@code GET_SIGNING_CERTIFICATES} and then searching through the resulting {@code
+ * signingInfo} field to see if the desired certificate is present.
+ *
+ * @param packageName package whose signing certificates to check
+ * @param certificate signing certificate for which to search
+ * @param type representation of the {@code certificate}
+ * @return true if this package was or is signed by exactly the certificate {@code certificate}
+ */
+ public boolean hasSigningCertificate(
+ String packageName, byte[] certificate, @CertificateInputType int type) {
+ throw new UnsupportedOperationException(
+ "hasSigningCertificate not implemented in subclass");
+ }
+
+ /**
+ * Searches the set of signing certificates by which the package(s) for the given uid has proven
+ * to have been signed. For multiple packages sharing the same uid, this will return the
+ * signing certificates found in the signing history of the "newest" package, where "newest"
+ * indicates the package with the newest signing certificate in the shared uid group. This
+ * method should be used instead of {@code getPackageInfo} with {@code GET_SIGNATURES}
+ * since it takes into account the possibility of signing certificate rotation, except in the
+ * case of packages that are signed by multiple certificates, for which signing certificate
+ * rotation is not supported. This method is analogous to using {@code getPackagesForUid}
+ * followed by {@code getPackageInfo} with {@code GET_SIGNING_CERTIFICATES}, selecting the
+ * {@code PackageInfo} of the newest-signed bpackage , and finally searching through the
+ * resulting {@code signingInfo} field to see if the desired certificate is there.
+ *
+ * @param uid uid whose signing certificates to check
+ * @param certificate signing certificate for which to search
+ * @param type representation of the {@code certificate}
+ * @return true if this package was or is signed by exactly the certificate {@code certificate}
+ */
+ public boolean hasSigningCertificate(
+ int uid, byte[] certificate, @CertificateInputType int type) {
+ throw new UnsupportedOperationException(
+ "hasSigningCertificate not implemented in subclass");
+ }
+
+ /**
+ * @return the system defined text classifier package name, or null if there's none.
+ *
+ * @hide
+ */
+ public String getSystemTextClassifierPackageName() {
+ throw new UnsupportedOperationException(
+ "getSystemTextClassifierPackageName not implemented in subclass");
+ }
+
+ /**
+ * @return whether a given package's state is protected, e.g. package cannot be disabled,
+ * suspended, hidden or force stopped.
+ *
+ * @hide
+ */
+ public boolean isPackageStateProtected(String packageName, int userId) {
+ throw new UnsupportedOperationException(
+ "isPackageStateProtected not implemented in subclass");
+ }
+
}
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 4c981cdb2511..f30b3fee7f46 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -16,6 +16,9 @@
package android.content.pm;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager.ApplicationInfoFlags;
@@ -23,8 +26,11 @@ import android.content.pm.PackageManager.ComponentInfoFlags;
import android.content.pm.PackageManager.PackageInfoFlags;
import android.content.pm.PackageManager.ResolveInfoFlags;
import android.os.Bundle;
+import android.os.PersistableBundle;
import android.util.SparseArray;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.List;
/**
@@ -33,6 +39,30 @@ import java.util.List;
* @hide Only for use within the system server.
*/
public abstract class PackageManagerInternal {
+ public static final int PACKAGE_SYSTEM = 0;
+ public static final int PACKAGE_SETUP_WIZARD = 1;
+ public static final int PACKAGE_INSTALLER = 2;
+ public static final int PACKAGE_VERIFIER = 3;
+ public static final int PACKAGE_BROWSER = 4;
+ public static final int PACKAGE_SYSTEM_TEXT_CLASSIFIER = 5;
+ @IntDef(value = {
+ PACKAGE_SYSTEM,
+ PACKAGE_SETUP_WIZARD,
+ PACKAGE_INSTALLER,
+ PACKAGE_VERIFIER,
+ PACKAGE_BROWSER,
+ PACKAGE_SYSTEM_TEXT_CLASSIFIER,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface KnownPackage {}
+
+ /** Observer called whenever the list of packages changes */
+ public interface PackageListObserver {
+ /** A package was added to the system. */
+ void onPackageAdded(@NonNull String packageName);
+ /** A package was removed from the system. */
+ void onPackageRemoved(@NonNull String packageName);
+ }
/**
* Provider for package names.
@@ -92,6 +122,12 @@ public abstract class PackageManagerInternal {
public abstract void setSimCallManagerPackagesProvider(PackagesProvider provider);
/**
+ * Sets the Use Open Wifi packages provider.
+ * @param provider The packages provider.
+ */
+ public abstract void setUseOpenWifiAppPackagesProvider(PackagesProvider provider);
+
+ /**
* Sets the sync adapter packages provider.
* @param provider The provider.
*/
@@ -120,6 +156,14 @@ public abstract class PackageManagerInternal {
int userId);
/**
+ * Requests granting of the default permissions to the current default Use Open Wifi app.
+ * @param packageName The default use open wifi package name.
+ * @param userId The user for which to grant the permissions.
+ */
+ public abstract void grantDefaultPermissionsToDefaultUseOpenWifiApp(String packageName,
+ int userId);
+
+ /**
* Sets a list of apps to keep in PM's internal data structures and as APKs even if no user has
* currently installed it. The apps are not preloaded.
* @param packageList List of package names to keep cached.
@@ -145,6 +189,62 @@ public abstract class PackageManagerInternal {
@PackageInfoFlags int flags, int filterCallingUid, int userId);
/**
+ * Retrieve launcher extras for a suspended package provided to the system in
+ * {@link PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
+ * PersistableBundle, String)}.
+ *
+ * @param packageName The package for which to return launcher extras.
+ * @param userId The user for which to check.
+ * @return The launcher extras.
+ *
+ * @see PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
+ * PersistableBundle, String)
+ * @see PackageManager#isPackageSuspended()
+ */
+ public abstract Bundle getSuspendedPackageLauncherExtras(String packageName,
+ int userId);
+
+ /**
+ * Internal api to query the suspended state of a package.
+ * @param packageName The package to check.
+ * @param userId The user id to check for.
+ * @return {@code true} if the package is suspended, {@code false} otherwise.
+ * @see PackageManager#isPackageSuspended(String)
+ */
+ public abstract boolean isPackageSuspended(String packageName, int userId);
+
+ /**
+ * Get the name of the package that suspended the given package. Packages can be suspended by
+ * device administrators or apps holding {@link android.Manifest.permission#MANAGE_USERS} or
+ * {@link android.Manifest.permission#SUSPEND_APPS}.
+ *
+ * @param suspendedPackage The package that has been suspended.
+ * @param userId The user for which to check.
+ * @return Name of the package that suspended the given package. Returns {@code null} if the
+ * given package is not currently suspended and the platform package name - i.e.
+ * {@code "android"} - if the package was suspended by a device admin.
+ */
+ public abstract String getSuspendingPackage(String suspendedPackage, int userId);
+
+ /**
+ * Get the dialog message to be shown to the user when they try to launch a suspended
+ * application.
+ *
+ * @param suspendedPackage The package that has been suspended.
+ * @param userId The user for which to check.
+ * @return The dialog message to be shown to the user.
+ */
+ public abstract String getSuspendedDialogMessage(String suspendedPackage, int userId);
+
+ /**
+ * Do a straight uid lookup for the given package/application in the given user.
+ * @see PackageManager#getPackageUidAsUser(String, int, int)
+ * @return The app's uid, or < 0 if the package was not found in that user
+ */
+ public abstract int getPackageUid(String packageName,
+ @PackageInfoFlags int flags, int userId);
+
+ /**
* Retrieve all of the information we know about a particular package/application.
* @param filterCallingUid The results will be filtered in the context of this UID instead
* of the calling UID.
@@ -172,12 +272,24 @@ public abstract class PackageManagerInternal {
@ResolveInfoFlags int flags, int filterCallingUid, int userId);
/**
+ * Retrieve all services that can be performed for the given intent.
+ * @see PackageManager#queryIntentServices(Intent, int)
+ */
+ public abstract List<ResolveInfo> queryIntentServices(
+ Intent intent, int flags, int callingUid, int userId);
+
+ /**
* Interface to {@link com.android.server.pm.PackageManagerService#getHomeActivitiesAsUser}.
*/
public abstract ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
int userId);
/**
+ * @return The default home activity component name.
+ */
+ public abstract ComponentName getDefaultHomeActivity(int userId);
+
+ /**
* Called by DeviceOwnerManagerService to set the package names of device owner and profile
* owners.
*/
@@ -190,6 +302,12 @@ public abstract class PackageManagerInternal {
public abstract boolean isPackageDataProtected(int userId, String packageName);
/**
+ * Returns {@code true} if a given package's state is protected, e.g. it cannot be force
+ * stopped, suspended, disabled or hidden. Otherwise, returns {@code false}.
+ */
+ public abstract boolean isPackageStateProtected(String packageName, int userId);
+
+ /**
* Returns {@code true} if a given package is installed as ephemeral. Otherwise, returns
* {@code false}.
*/
@@ -311,6 +429,12 @@ public abstract class PackageManagerInternal {
public abstract boolean isPackagePersistent(String packageName);
/**
+ * Returns whether or not the given package represents a legacy system application released
+ * prior to runtime permissions.
+ */
+ public abstract boolean isLegacySystemApp(PackageParser.Package pkg);
+
+ /**
* Get all overlay packages for a user.
* @param userId The user for which to get the overlays.
* @return A list of overlay packages. An empty list is returned if the
@@ -343,14 +467,19 @@ public abstract class PackageManagerInternal {
* Resolves an activity intent, allowing instant apps to be resolved.
*/
public abstract ResolveInfo resolveIntent(Intent intent, String resolvedType,
- int flags, int userId);
+ int flags, int userId, boolean resolveForStart, int filterCallingUid);
/**
* Resolves a service intent, allowing instant apps to be resolved.
*/
- public abstract ResolveInfo resolveService(Intent intent, String resolvedType,
+ public abstract ResolveInfo resolveService(Intent intent, String resolvedType,
int flags, int userId, int callingUid);
+ /**
+ * Resolves a content provider intent.
+ */
+ public abstract ProviderInfo resolveContentProvider(String name, int flags, int userId);
+
/**
* Track the creator of a new isolated uid.
* @param isolatedUid The newly created isolated uid.
@@ -369,9 +498,17 @@ public abstract class PackageManagerInternal {
*/
public abstract int getUidTargetSdkVersion(int uid);
+ /**
+ * Return the taget SDK version for the app with the given package name.
+ */
+ public abstract int getPackageTargetSdkVersion(String packageName);
+
/** Whether the binder caller can access instant apps. */
public abstract boolean canAccessInstantApps(int callingUid, int userId);
+ /** Whether the binder caller can access the given component. */
+ public abstract boolean canAccessComponent(int callingUid, ComponentName component, int userId);
+
/**
* Returns {@code true} if a given package has instant application meta-data.
* Otherwise, returns {@code false}. Meta-data is state (eg. cookie, app icon, etc)
@@ -383,4 +520,112 @@ public abstract class PackageManagerInternal {
* Updates a package last used time.
*/
public abstract void notifyPackageUse(String packageName, int reason);
+
+ /**
+ * Returns a package object for the given package name.
+ */
+ public abstract @Nullable PackageParser.Package getPackage(@NonNull String packageName);
+
+ /**
+ * Returns a list without a change observer.
+ *
+ * {@see #getPackageList(PackageListObserver)}
+ */
+ public @NonNull PackageList getPackageList() {
+ return getPackageList(null);
+ }
+
+ /**
+ * Returns the list of packages installed at the time of the method call.
+ * <p>The given observer is notified when the list of installed packages
+ * changes [eg. a package was installed or uninstalled]. It will not be
+ * notified if a package is updated.
+ * <p>The package list will not be updated automatically as packages are
+ * installed / uninstalled. Any changes must be handled within the observer.
+ */
+ public abstract @NonNull PackageList getPackageList(@Nullable PackageListObserver observer);
+
+ /**
+ * Removes the observer.
+ * <p>Generally not needed. {@link #getPackageList(PackageListObserver)} will automatically
+ * remove the observer.
+ * <p>Does nothing if the observer isn't currently registered.
+ * <p>Observers are notified asynchronously and it's possible for an observer to be
+ * invoked after its been removed.
+ */
+ public abstract void removePackageListObserver(@NonNull PackageListObserver observer);
+
+ /**
+ * Returns a package object for the disabled system package name.
+ */
+ public abstract @Nullable PackageParser.Package getDisabledPackage(@NonNull String packageName);
+
+ /**
+ * Returns whether or not the component is the resolver activity.
+ */
+ public abstract boolean isResolveActivityComponent(@NonNull ComponentInfo component);
+
+ /**
+ * Returns the package name for a known package.
+ */
+ public abstract @Nullable String getKnownPackageName(
+ @KnownPackage int knownPackage, int userId);
+
+ /**
+ * Returns whether the package is an instant app.
+ */
+ public abstract boolean isInstantApp(String packageName, int userId);
+
+ /**
+ * Returns whether the package is an instant app.
+ */
+ public abstract @Nullable String getInstantAppPackageName(int uid);
+
+ /**
+ * Returns whether or not access to the application should be filtered.
+ * <p>
+ * Access may be limited based upon whether the calling or target applications
+ * are instant applications.
+ *
+ * @see #canAccessInstantApps(int)
+ */
+ public abstract boolean filterAppAccess(
+ @Nullable PackageParser.Package pkg, int callingUid, int userId);
+
+ /*
+ * NOTE: The following methods are temporary until permissions are extracted from
+ * the package manager into a component specifically for handling permissions.
+ */
+ /** Returns the flags for the given permission. */
+ public abstract @Nullable int getPermissionFlagsTEMP(@NonNull String permName,
+ @NonNull String packageName, int userId);
+ /** Updates the flags for the given permission. */
+ public abstract void updatePermissionFlagsTEMP(@NonNull String permName,
+ @NonNull String packageName, int flagMask, int flagValues, int userId);
+
+ /**
+ * Returns true if it's still safe to restore data backed up from this app's version
+ * that was signed with restoringFromSigHash.
+ */
+ public abstract boolean isDataRestoreSafe(@NonNull byte[] restoringFromSigHash,
+ @NonNull String packageName);
+
+ /**
+ * Returns true if it's still safe to restore data backed up from this app's version
+ * that was signed with restoringFromSig.
+ */
+ public abstract boolean isDataRestoreSafe(@NonNull Signature restoringFromSig,
+ @NonNull String packageName);
+
+
+ /**
+ * Returns true if the the signing information for {@code clientUid} is sufficient to gain
+ * access gated by {@code capability}. This can happen if the two UIDs have the same signing
+ * information, if the signing information {@code clientUid} indicates that it has the signing
+ * certificate for {@code serverUid} in its signing history (if it was previously signed by it),
+ * or if the signing certificate for {@code clientUid} is in ths signing history for {@code
+ * serverUid} and with the {@code capability} specified.
+ */
+ public abstract boolean hasSignatureCapability(int serverUid, int clientUid,
+ @PackageParser.SigningDetails.CertCapabilities int capability);
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index a22f6d61b39f..3f8a390c215e 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -32,16 +32,15 @@ import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
import static android.os.Build.VERSION_CODES.O;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
+import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -55,6 +54,7 @@ import android.content.pm.PackageParserCacheHelper.WriteHelper;
import android.content.pm.split.DefaultSplitAssetLoader;
import android.content.pm.split.SplitAssetDependencyLoader;
import android.content.pm.split.SplitAssetLoader;
+import android.content.res.ApkAssets;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -79,14 +79,15 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.Base64;
+import android.util.ByteStringUtils;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.util.PackageUtils;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TypedValue;
-import android.util.apk.ApkSignatureSchemeV2Verifier;
-import android.util.jar.StrictJarFile;
+import android.util.apk.ApkSignatureVerifier;
import android.view.Gravity;
import com.android.internal.R;
@@ -102,17 +103,17 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.InputStream;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Constructor;
-import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
import java.security.spec.EncodedKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
@@ -125,8 +126,6 @@ import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.zip.ZipEntry;
/**
* Parser for package files (APKs) on disk. This supports apps packaged either
@@ -159,19 +158,13 @@ public class PackageParser {
private static final boolean MULTI_PACKAGE_APK_ENABLED = Build.IS_DEBUGGABLE &&
SystemProperties.getBoolean(PROPERTY_CHILD_PACKAGES_ENABLED, false);
- private static final int MAX_PACKAGES_PER_APK = 5;
-
- public static final int APK_SIGNING_UNKNOWN = 0;
- public static final int APK_SIGNING_V1 = 1;
- public static final int APK_SIGNING_V2 = 2;
-
private static final float DEFAULT_PRE_O_MAX_ASPECT_RATIO = 1.86f;
// TODO: switch outError users to PackageParserException
// TODO: refactor "codePath" to "apkPath"
/** File name in an APK for the Android manifest. */
- private static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
+ public static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
/** Path prefix for apps on expanded storage */
private static final String MNT_EXPAND = "/mnt/expand/";
@@ -204,10 +197,6 @@ public class PackageParser {
private static final String TAG_RESTRICT_UPDATE = "restrict-update";
private static final String TAG_USES_SPLIT = "uses-split";
- // [b/36551762] STOPSHIP remove the ability to expose components via meta-data
- // Temporary workaround; allow meta-data to expose components to instant apps
- private static final String META_DATA_INSTANT_APPS = "instantapps.clients.allowed";
-
private static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect";
/**
@@ -395,6 +384,7 @@ public class PackageParser {
public static class PackageLite {
public final String packageName;
public final int versionCode;
+ public final int versionCodeMajor;
public final int installLocation;
public final VerifierInfo[] verifiers;
@@ -437,6 +427,7 @@ public class PackageParser {
String[] splitCodePaths, int[] splitRevisionCodes) {
this.packageName = baseApk.packageName;
this.versionCode = baseApk.versionCode;
+ this.versionCodeMajor = baseApk.versionCodeMajor;
this.installLocation = baseApk.installLocation;
this.verifiers = baseApk.verifiers;
this.splitNames = splitNames;
@@ -477,11 +468,11 @@ public class PackageParser {
public final String configForSplit;
public final String usesSplitName;
public final int versionCode;
+ public final int versionCodeMajor;
public final int revisionCode;
public final int installLocation;
public final VerifierInfo[] verifiers;
- public final Signature[] signatures;
- public final Certificate[][] certificates;
+ public final SigningDetails signingDetails;
public final boolean coreApp;
public final boolean debuggable;
public final boolean multiArch;
@@ -489,12 +480,13 @@ public class PackageParser {
public final boolean extractNativeLibs;
public final boolean isolatedSplits;
- public ApkLite(String codePath, String packageName, String splitName, boolean isFeatureSplit,
- String configForSplit, String usesSplitName, int versionCode, int revisionCode,
- int installLocation, List<VerifierInfo> verifiers, Signature[] signatures,
- Certificate[][] certificates, boolean coreApp, boolean debuggable,
- boolean multiArch, boolean use32bitAbi, boolean extractNativeLibs,
- boolean isolatedSplits) {
+ public ApkLite(String codePath, String packageName, String splitName,
+ boolean isFeatureSplit,
+ String configForSplit, String usesSplitName, int versionCode, int versionCodeMajor,
+ int revisionCode, int installLocation, List<VerifierInfo> verifiers,
+ SigningDetails signingDetails, boolean coreApp,
+ boolean debuggable, boolean multiArch, boolean use32bitAbi,
+ boolean extractNativeLibs, boolean isolatedSplits) {
this.codePath = codePath;
this.packageName = packageName;
this.splitName = splitName;
@@ -502,11 +494,11 @@ public class PackageParser {
this.configForSplit = configForSplit;
this.usesSplitName = usesSplitName;
this.versionCode = versionCode;
+ this.versionCodeMajor = versionCodeMajor;
this.revisionCode = revisionCode;
this.installLocation = installLocation;
+ this.signingDetails = signingDetails;
this.verifiers = verifiers.toArray(new VerifierInfo[verifiers.size()]);
- this.signatures = signatures;
- this.certificates = certificates;
this.coreApp = coreApp;
this.debuggable = debuggable;
this.multiArch = multiArch;
@@ -514,6 +506,10 @@ public class PackageParser {
this.extractNativeLibs = extractNativeLibs;
this.isolatedSplits = isolatedSplits;
}
+
+ public long getLongVersionCode() {
+ return PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode);
+ }
}
/**
@@ -664,6 +660,7 @@ public class PackageParser {
pi.packageName = p.packageName;
pi.splitNames = p.splitNames;
pi.versionCode = p.mVersionCode;
+ pi.versionCodeMajor = p.mVersionCodeMajor;
pi.baseRevisionCode = p.baseRevisionCode;
pi.splitRevisionCodes = p.splitRevisionCodes;
pi.versionName = p.mVersionName;
@@ -680,8 +677,11 @@ public class PackageParser {
pi.restrictedAccountType = p.mRestrictedAccountType;
pi.requiredAccountType = p.mRequiredAccountType;
pi.overlayTarget = p.mOverlayTarget;
+ pi.overlayCategory = p.mOverlayCategory;
pi.overlayPriority = p.mOverlayPriority;
- pi.isStaticOverlay = p.mIsStaticOverlay;
+ pi.mOverlayIsStatic = p.mOverlayIsStatic;
+ pi.compileSdkVersion = p.mCompileSdkVersion;
+ pi.compileSdkVersionCodename = p.mCompileSdkVersionCodename;
pi.firstInstallTime = firstInstallTime;
pi.lastUpdateTime = lastUpdateTime;
if ((flags&PackageManager.GET_GIDS) != 0) {
@@ -793,48 +793,58 @@ public class PackageParser {
}
}
}
+ // deprecated method of getting signing certificates
if ((flags&PackageManager.GET_SIGNATURES) != 0) {
- int N = (p.mSignatures != null) ? p.mSignatures.length : 0;
- if (N > 0) {
- pi.signatures = new Signature[N];
- System.arraycopy(p.mSignatures, 0, pi.signatures, 0, N);
+ if (p.mSigningDetails.hasPastSigningCertificates()) {
+ // Package has included signing certificate rotation information. Return the oldest
+ // cert so that programmatic checks keep working even if unaware of key rotation.
+ pi.signatures = new Signature[1];
+ pi.signatures[0] = p.mSigningDetails.pastSigningCertificates[0];
+ } else if (p.mSigningDetails.hasSignatures()) {
+ // otherwise keep old behavior
+ int numberOfSigs = p.mSigningDetails.signatures.length;
+ pi.signatures = new Signature[numberOfSigs];
+ System.arraycopy(p.mSigningDetails.signatures, 0, pi.signatures, 0, numberOfSigs);
+ }
+ }
+
+ // replacement for GET_SIGNATURES
+ if ((flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) {
+ if (p.mSigningDetails != SigningDetails.UNKNOWN) {
+ // only return a valid SigningInfo if there is signing information to report
+ pi.signingInfo = new SigningInfo(p.mSigningDetails);
+ } else {
+ pi.signingInfo = null;
}
}
return pi;
}
- private static Certificate[][] loadCertificates(StrictJarFile jarFile, ZipEntry entry)
- throws PackageParserException {
- InputStream is = null;
- try {
- // We must read the stream for the JarEntry to retrieve
- // its certificates.
- is = jarFile.getInputStream(entry);
- readFullyIgnoringContents(is);
- return jarFile.getCertificateChains(entry);
- } catch (IOException | RuntimeException e) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
- "Failed reading " + entry.getName() + " in " + jarFile, e);
- } finally {
- IoUtils.closeQuietly(is);
- }
- }
-
- public final static int PARSE_IS_SYSTEM = 1<<0;
- public final static int PARSE_CHATTY = 1<<1;
- public final static int PARSE_MUST_BE_APK = 1<<2;
- public final static int PARSE_IGNORE_PROCESSES = 1<<3;
- public final static int PARSE_FORWARD_LOCK = 1<<4;
- public final static int PARSE_EXTERNAL_STORAGE = 1<<5;
- public final static int PARSE_IS_SYSTEM_DIR = 1<<6;
- public final static int PARSE_IS_PRIVILEGED = 1<<7;
- public final static int PARSE_COLLECT_CERTIFICATES = 1<<8;
- public final static int PARSE_TRUSTED_OVERLAY = 1<<9;
- public final static int PARSE_ENFORCE_CODE = 1<<10;
- /** @deprecated remove when fixing b/34761192 */
+ public static final int PARSE_MUST_BE_APK = 1 << 0;
+ public static final int PARSE_IGNORE_PROCESSES = 1 << 1;
+ /** @deprecated forward lock no longer functional. remove. */
@Deprecated
- public final static int PARSE_IS_EPHEMERAL = 1<<11;
- public final static int PARSE_FORCE_SDK = 1<<12;
+ public static final int PARSE_FORWARD_LOCK = 1 << 2;
+ public static final int PARSE_EXTERNAL_STORAGE = 1 << 3;
+ public static final int PARSE_IS_SYSTEM_DIR = 1 << 4;
+ public static final int PARSE_COLLECT_CERTIFICATES = 1 << 5;
+ public static final int PARSE_ENFORCE_CODE = 1 << 6;
+ public static final int PARSE_FORCE_SDK = 1 << 7;
+ public static final int PARSE_CHATTY = 1 << 31;
+
+ @IntDef(flag = true, prefix = { "PARSE_" }, value = {
+ PARSE_CHATTY,
+ PARSE_COLLECT_CERTIFICATES,
+ PARSE_ENFORCE_CODE,
+ PARSE_EXTERNAL_STORAGE,
+ PARSE_FORCE_SDK,
+ PARSE_FORWARD_LOCK,
+ PARSE_IGNORE_PROCESSES,
+ PARSE_IS_SYSTEM_DIR,
+ PARSE_MUST_BE_APK,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ParseFlags {}
private static final Comparator<String> sSplitNameComparator = new SplitNameComparator();
@@ -1245,9 +1255,12 @@ public class PackageParser {
}
}
- pkg.setCodePath(packageDir.getAbsolutePath());
+ pkg.setCodePath(packageDir.getCanonicalPath());
pkg.setUse32bitAbi(lite.use32bitAbi);
return pkg;
+ } catch (IOException e) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+ "Failed to get path: " + lite.baseCodePath, e);
} finally {
IoUtils.closeQuietly(assetLoader);
}
@@ -1265,7 +1278,6 @@ public class PackageParser {
*/
@Deprecated
public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
- final AssetManager assets = newConfiguredAssetManager();
final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);
if (mOnlyCoreApps) {
if (!lite.coreApp) {
@@ -1274,32 +1286,18 @@ public class PackageParser {
}
}
+ final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
try {
- final Package pkg = parseBaseApk(apkFile, assets, flags);
- pkg.setCodePath(apkFile.getAbsolutePath());
+ final Package pkg = parseBaseApk(apkFile, assetLoader.getBaseAssetManager(), flags);
+ pkg.setCodePath(apkFile.getCanonicalPath());
pkg.setUse32bitAbi(lite.use32bitAbi);
return pkg;
+ } catch (IOException e) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+ "Failed to get path: " + apkFile, e);
} finally {
- IoUtils.closeQuietly(assets);
- }
- }
-
- private static int loadApkIntoAssetManager(AssetManager assets, String apkPath, int flags)
- throws PackageParserException {
- if ((flags & PARSE_MUST_BE_APK) != 0 && !isApkPath(apkPath)) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
- "Invalid package file: " + apkPath);
- }
-
- // The AssetManager guarantees uniqueness for asset paths, so if this asset path
- // already exists in the AssetManager, addAssetPath will only return the cookie
- // assigned to it.
- int cookie = assets.addAssetPath(apkPath);
- if (cookie == 0) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
- "Failed adding asset path: " + apkPath);
+ IoUtils.closeQuietly(assetLoader);
}
- return cookie;
}
private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
@@ -1317,13 +1315,15 @@ public class PackageParser {
if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);
- final int cookie = loadApkIntoAssetManager(assets, apkPath, flags);
-
- Resources res = null;
XmlResourceParser parser = null;
try {
- res = new Resources(assets, mMetrics, null);
+ final int cookie = assets.findCookieForPath(apkPath);
+ if (cookie == 0) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+ "Failed adding asset path: " + apkPath);
+ }
parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
+ final Resources res = new Resources(assets, mMetrics, null);
final String[] outError = new String[1];
final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);
@@ -1335,7 +1335,7 @@ public class PackageParser {
pkg.setVolumeUuid(volumeUuid);
pkg.setApplicationVolumeUuid(volumeUuid);
pkg.setBaseCodePath(apkPath);
- pkg.setSignatures(null);
+ pkg.setSigningDetails(SigningDetails.UNKNOWN);
return pkg;
@@ -1358,15 +1358,18 @@ public class PackageParser {
if (DEBUG_JAR) Slog.d(TAG, "Scanning split APK: " + apkPath);
- final int cookie = loadApkIntoAssetManager(assets, apkPath, flags);
-
final Resources res;
XmlResourceParser parser = null;
try {
- res = new Resources(assets, mMetrics, null);
- assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- Build.VERSION.RESOURCES_SDK_INT);
+ // This must always succeed, as the path has been added to the AssetManager before.
+ final int cookie = assets.findCookieForPath(apkPath);
+ if (cookie == 0) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+ "Failed adding asset path: " + apkPath);
+ }
+
parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
+ res = new Resources(assets, mMetrics, null);
final String[] outError = new String[1];
pkg = parseSplitApk(pkg, res, parser, flags, splitIndex, outError);
@@ -1455,84 +1458,42 @@ public class PackageParser {
return pkg;
}
- public static int getApkSigningVersion(Package pkg) {
- try {
- if (ApkSignatureSchemeV2Verifier.hasSignature(pkg.baseCodePath)) {
- return APK_SIGNING_V2;
- }
- return APK_SIGNING_V1;
- } catch (IOException e) {
- }
- return APK_SIGNING_UNKNOWN;
- }
-
- /**
- * Populates the correct packages fields with the given certificates.
- * <p>
- * This is useful when we've already processed the certificates [such as during package
- * installation through an installer session]. We don't re-process the archive and
- * simply populate the correct fields.
- */
- public static void populateCertificates(Package pkg, Certificate[][] certificates)
- throws PackageParserException {
- pkg.mCertificates = null;
- pkg.mSignatures = null;
- pkg.mSigningKeys = null;
-
- pkg.mCertificates = certificates;
- try {
- pkg.mSignatures = convertToSignatures(certificates);
- } catch (CertificateEncodingException e) {
- // certificates weren't encoded properly; something went wrong
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
- "Failed to collect certificates from " + pkg.baseCodePath, e);
- }
- pkg.mSigningKeys = new ArraySet<>(certificates.length);
- for (int i = 0; i < certificates.length; i++) {
- Certificate[] signerCerts = certificates[i];
- Certificate signerCert = signerCerts[0];
- pkg.mSigningKeys.add(signerCert.getPublicKey());
- }
- // add signatures to child packages
- final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
- for (int i = 0; i < childCount; i++) {
- Package childPkg = pkg.childPackages.get(i);
- childPkg.mCertificates = pkg.mCertificates;
- childPkg.mSignatures = pkg.mSignatures;
- childPkg.mSigningKeys = pkg.mSigningKeys;
+ /** Parses the public keys from the set of signatures. */
+ public static ArraySet<PublicKey> toSigningKeys(Signature[] signatures)
+ throws CertificateException {
+ ArraySet<PublicKey> keys = new ArraySet<>(signatures.length);
+ for (int i = 0; i < signatures.length; i++) {
+ keys.add(signatures[i].getPublicKey());
}
+ return keys;
}
/**
* Collect certificates from all the APKs described in the given package,
- * populating {@link Package#mSignatures}. Also asserts that all APK
+ * populating {@link Package#mSigningDetails}. Also asserts that all APK
* contents are signed correctly and consistently.
*/
- public static void collectCertificates(Package pkg, int parseFlags)
+ public static void collectCertificates(Package pkg, boolean skipVerify)
throws PackageParserException {
- collectCertificatesInternal(pkg, parseFlags);
+ collectCertificatesInternal(pkg, skipVerify);
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
Package childPkg = pkg.childPackages.get(i);
- childPkg.mCertificates = pkg.mCertificates;
- childPkg.mSignatures = pkg.mSignatures;
- childPkg.mSigningKeys = pkg.mSigningKeys;
+ childPkg.mSigningDetails = pkg.mSigningDetails;
}
}
- private static void collectCertificatesInternal(Package pkg, int parseFlags)
+ private static void collectCertificatesInternal(Package pkg, boolean skipVerify)
throws PackageParserException {
- pkg.mCertificates = null;
- pkg.mSignatures = null;
- pkg.mSigningKeys = null;
+ pkg.mSigningDetails = SigningDetails.UNKNOWN;
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
try {
- collectCertificates(pkg, new File(pkg.baseCodePath), parseFlags);
+ collectCertificates(pkg, new File(pkg.baseCodePath), skipVerify);
if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
for (int i = 0; i < pkg.splitCodePaths.length; i++) {
- collectCertificates(pkg, new File(pkg.splitCodePaths[i]), parseFlags);
+ collectCertificates(pkg, new File(pkg.splitCodePaths[i]), skipVerify);
}
}
} finally {
@@ -1540,159 +1501,36 @@ public class PackageParser {
}
}
- private static void collectCertificates(Package pkg, File apkFile, int parseFlags)
+ private static void collectCertificates(Package pkg, File apkFile, boolean skipVerify)
throws PackageParserException {
final String apkPath = apkFile.getAbsolutePath();
- // Try to verify the APK using APK Signature Scheme v2.
- boolean verified = false;
- {
- Certificate[][] allSignersCerts = null;
- Signature[] signatures = null;
- try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifyV2");
- allSignersCerts = ApkSignatureSchemeV2Verifier.verify(apkPath);
- signatures = convertToSignatures(allSignersCerts);
- // APK verified using APK Signature Scheme v2.
- verified = true;
- } catch (ApkSignatureSchemeV2Verifier.SignatureNotFoundException e) {
- // No APK Signature Scheme v2 signature found
- if ((parseFlags & PARSE_IS_EPHEMERAL) != 0) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
- "No APK Signature Scheme v2 signature in ephemeral package " + apkPath,
- e);
- }
- // Static shared libraries must use only the V2 signing scheme
- if (pkg.applicationInfo.isStaticSharedLibrary()) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
- "Static shared libs must use v2 signature scheme " + apkPath);
- }
- } catch (Exception e) {
- // APK Signature Scheme v2 signature was found but did not verify
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
- "Failed to collect certificates from " + apkPath
- + " using APK Signature Scheme v2",
- e);
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
-
- if (verified) {
- if (pkg.mCertificates == null) {
- pkg.mCertificates = allSignersCerts;
- pkg.mSignatures = signatures;
- pkg.mSigningKeys = new ArraySet<>(allSignersCerts.length);
- for (int i = 0; i < allSignersCerts.length; i++) {
- Certificate[] signerCerts = allSignersCerts[i];
- Certificate signerCert = signerCerts[0];
- pkg.mSigningKeys.add(signerCert.getPublicKey());
- }
- } else {
- if (!Signature.areExactMatch(pkg.mSignatures, signatures)) {
- throw new PackageParserException(
- INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
- apkPath + " has mismatched certificates");
- }
- }
- // Not yet done, because we need to confirm that AndroidManifest.xml exists and,
- // if requested, that classes.dex exists.
- }
+ int minSignatureScheme = SigningDetails.SignatureSchemeVersion.JAR;
+ if (pkg.applicationInfo.isStaticSharedLibrary()) {
+ // must use v2 signing scheme
+ minSignatureScheme = SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2;
}
-
- StrictJarFile jarFile = null;
- try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "strictJarFileCtor");
- // Ignore signature stripping protections when verifying APKs from system partition.
- // For those APKs we only care about extracting signer certificates, and don't care
- // about verifying integrity.
- boolean signatureSchemeRollbackProtectionsEnforced =
- (parseFlags & PARSE_IS_SYSTEM_DIR) == 0;
- jarFile = new StrictJarFile(
- apkPath,
- !verified, // whether to verify JAR signature
- signatureSchemeRollbackProtectionsEnforced);
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-
- // Always verify manifest, regardless of source
- final ZipEntry manifestEntry = jarFile.findEntry(ANDROID_MANIFEST_FILENAME);
- if (manifestEntry == null) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
- "Package " + apkPath + " has no manifest");
- }
-
- // Optimization: early termination when APK already verified
- if (verified) {
- return;
- }
-
- // APK's integrity needs to be verified using JAR signature scheme.
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifyV1");
- final List<ZipEntry> toVerify = new ArrayList<>();
- toVerify.add(manifestEntry);
-
- // If we're parsing an untrusted package, verify all contents
- if ((parseFlags & PARSE_IS_SYSTEM_DIR) == 0) {
- final Iterator<ZipEntry> i = jarFile.iterator();
- while (i.hasNext()) {
- final ZipEntry entry = i.next();
-
- if (entry.isDirectory()) continue;
-
- final String entryName = entry.getName();
- if (entryName.startsWith("META-INF/")) continue;
- if (entryName.equals(ANDROID_MANIFEST_FILENAME)) continue;
-
- toVerify.add(entry);
- }
- }
-
- // Verify that entries are signed consistently with the first entry
- // we encountered. Note that for splits, certificates may have
- // already been populated during an earlier parse of a base APK.
- for (ZipEntry entry : toVerify) {
- final Certificate[][] entryCerts = loadCertificates(jarFile, entry);
- if (ArrayUtils.isEmpty(entryCerts)) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
- "Package " + apkPath + " has no certificates at entry "
- + entry.getName());
- }
- final Signature[] entrySignatures = convertToSignatures(entryCerts);
-
- if (pkg.mCertificates == null) {
- pkg.mCertificates = entryCerts;
- pkg.mSignatures = entrySignatures;
- pkg.mSigningKeys = new ArraySet<PublicKey>();
- for (int i=0; i < entryCerts.length; i++) {
- pkg.mSigningKeys.add(entryCerts[i][0].getPublicKey());
- }
- } else {
- if (!Signature.areExactMatch(pkg.mSignatures, entrySignatures)) {
- throw new PackageParserException(
- INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES, "Package " + apkPath
- + " has mismatched certificates at entry "
- + entry.getName());
- }
- }
- }
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- } catch (GeneralSecurityException e) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
- "Failed to collect certificates from " + apkPath, e);
- } catch (IOException | RuntimeException e) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
- "Failed to collect certificates from " + apkPath, e);
- } finally {
- closeQuietly(jarFile);
+ SigningDetails verified;
+ if (skipVerify) {
+ // systemDir APKs are already trusted, save time by not verifying
+ verified = ApkSignatureVerifier.plsCertsNoVerifyOnlyCerts(
+ apkPath, minSignatureScheme);
+ } else {
+ verified = ApkSignatureVerifier.verify(apkPath, minSignatureScheme);
}
- }
- private static Signature[] convertToSignatures(Certificate[][] certs)
- throws CertificateEncodingException {
- final Signature[] res = new Signature[certs.length];
- for (int i = 0; i < certs.length; i++) {
- res[i] = new Signature(certs[i]);
+ // Verify that entries are signed consistently with the first pkg
+ // we encountered. Note that for splits, certificates may have
+ // already been populated during an earlier parse of a base APK.
+ if (pkg.mSigningDetails == SigningDetails.UNKNOWN) {
+ pkg.mSigningDetails = verified;
+ } else {
+ if (!Signature.areExactMatch(pkg.mSigningDetails.signatures, verified.signatures)) {
+ throw new PackageParserException(
+ INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
+ apkPath + " has mismatched certificates");
+ }
}
- return res;
}
private static AssetManager newConfiguredAssetManager() {
@@ -1712,43 +1550,59 @@ public class PackageParser {
*/
public static ApkLite parseApkLite(File apkFile, int flags)
throws PackageParserException {
- final String apkPath = apkFile.getAbsolutePath();
+ return parseApkLiteInner(apkFile, null, null, flags);
+ }
+
+ /**
+ * Utility method that retrieves lightweight details about a single APK
+ * file, including package name, split name, and install location.
+ *
+ * @param fd already open file descriptor of an apk file
+ * @param debugPathName arbitrary text name for this file, for debug output
+ * @param flags optional parse flags, such as
+ * {@link #PARSE_COLLECT_CERTIFICATES}
+ */
+ public static ApkLite parseApkLite(FileDescriptor fd, String debugPathName, int flags)
+ throws PackageParserException {
+ return parseApkLiteInner(null, fd, debugPathName, flags);
+ }
+
+ private static ApkLite parseApkLiteInner(File apkFile, FileDescriptor fd, String debugPathName,
+ int flags) throws PackageParserException {
+ final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath();
- AssetManager assets = null;
XmlResourceParser parser = null;
try {
- assets = newConfiguredAssetManager();
- int cookie = assets.addAssetPath(apkPath);
- if (cookie == 0) {
+ final ApkAssets apkAssets;
+ try {
+ apkAssets = fd != null
+ ? ApkAssets.loadFromFd(fd, debugPathName, false, false)
+ : ApkAssets.loadFromPath(apkPath);
+ } catch (IOException e) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
"Failed to parse " + apkPath);
}
- final DisplayMetrics metrics = new DisplayMetrics();
- metrics.setToDefaults();
+ parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME);
- parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
-
- final Signature[] signatures;
- final Certificate[][] certificates;
+ final SigningDetails signingDetails;
if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
// TODO: factor signature related items out of Package object
final Package tempPkg = new Package((String) null);
+ final boolean skipVerify = (flags & PARSE_IS_SYSTEM_DIR) != 0;
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
try {
- collectCertificates(tempPkg, apkFile, flags);
+ collectCertificates(tempPkg, apkFile, skipVerify);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
- signatures = tempPkg.mSignatures;
- certificates = tempPkg.mCertificates;
+ signingDetails = tempPkg.mSigningDetails;
} else {
- signatures = null;
- certificates = null;
+ signingDetails = SigningDetails.UNKNOWN;
}
final AttributeSet attrs = parser;
- return parseApkLite(apkPath, parser, attrs, signatures, certificates);
+ return parseApkLite(apkPath, parser, attrs, signingDetails);
} catch (XmlPullParserException | IOException | RuntimeException e) {
Slog.w(TAG, "Failed to parse " + apkPath, e);
@@ -1756,7 +1610,7 @@ public class PackageParser {
"Failed to parse " + apkPath, e);
} finally {
IoUtils.closeQuietly(parser);
- IoUtils.closeQuietly(assets);
+ // TODO(b/72056911): Implement and call close() on ApkAssets.
}
}
@@ -1835,12 +1689,13 @@ public class PackageParser {
}
private static ApkLite parseApkLite(String codePath, XmlPullParser parser, AttributeSet attrs,
- Signature[] signatures, Certificate[][] certificates)
+ SigningDetails signingDetails)
throws IOException, XmlPullParserException, PackageParserException {
final Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs);
int installLocation = PARSE_DEFAULT_INSTALL_LOCATION;
int versionCode = 0;
+ int versionCodeMajor = 0;
int revisionCode = 0;
boolean coreApp = false;
boolean debuggable = false;
@@ -1859,6 +1714,8 @@ public class PackageParser {
PARSE_DEFAULT_INSTALL_LOCATION);
} else if (attr.equals("versionCode")) {
versionCode = attrs.getAttributeIntValue(i, 0);
+ } else if (attr.equals("versionCodeMajor")) {
+ versionCodeMajor = attrs.getAttributeIntValue(i, 0);
} else if (attr.equals("revisionCode")) {
revisionCode = attrs.getAttributeIntValue(i, 0);
} else if (attr.equals("coreApp")) {
@@ -1924,9 +1781,9 @@ public class PackageParser {
}
return new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit,
- configForSplit, usesSplitName, versionCode, revisionCode, installLocation,
- verifiers, signatures, certificates, coreApp, debuggable, multiArch, use32bitAbi,
- extractNativeLibs, isolatedSplits);
+ configForSplit, usesSplitName, versionCode, versionCodeMajor, revisionCode,
+ installLocation, verifiers, signingDetails, coreApp, debuggable,
+ multiArch, use32bitAbi, extractNativeLibs, isolatedSplits);
}
/**
@@ -1946,14 +1803,6 @@ public class PackageParser {
*/
private boolean parseBaseApkChild(Package parentPkg, Resources res, XmlResourceParser parser,
int flags, String[] outError) throws XmlPullParserException, IOException {
- // Let ppl not abuse this mechanism by limiting the packages per APK
- if (parentPkg.childPackages != null && parentPkg.childPackages.size() + 2
- > MAX_PACKAGES_PER_APK) {
- outError[0] = "Maximum number of packages per APK is: " + MAX_PACKAGES_PER_APK;
- mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
- return false;
- }
-
// Make sure we have a valid child package name
String childPackageName = parser.getAttributeValue(null, "package");
if (validateName(childPackageName, true, false) != null) {
@@ -2055,8 +1904,11 @@ public class PackageParser {
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifest);
- pkg.mVersionCode = pkg.applicationInfo.versionCode = sa.getInteger(
+ pkg.mVersionCode = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
+ pkg.mVersionCodeMajor = sa.getInteger(
+ com.android.internal.R.styleable.AndroidManifest_versionCodeMajor, 0);
+ pkg.applicationInfo.setVersionCode(pkg.getLongVersionCode());
pkg.baseRevisionCode = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_revisionCode, 0);
pkg.mVersionName = sa.getNonConfigurationString(
@@ -2067,6 +1919,16 @@ public class PackageParser {
pkg.coreApp = parser.getAttributeBooleanValue(null, "coreApp", false);
+ pkg.mCompileSdkVersion = sa.getInteger(
+ com.android.internal.R.styleable.AndroidManifest_compileSdkVersion, 0);
+ pkg.applicationInfo.compileSdkVersion = pkg.mCompileSdkVersion;
+ pkg.mCompileSdkVersionCodename = sa.getNonConfigurationString(
+ com.android.internal.R.styleable.AndroidManifest_compileSdkVersionCodename, 0);
+ if (pkg.mCompileSdkVersionCodename != null) {
+ pkg.mCompileSdkVersionCodename = pkg.mCompileSdkVersionCodename.intern();
+ }
+ pkg.applicationInfo.compileSdkVersionCodename = pkg.mCompileSdkVersionCodename;
+
sa.recycle();
return parseBaseApkCommon(pkg, null, res, parser, flags, outError);
@@ -2105,11 +1967,6 @@ public class PackageParser {
String str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);
if (str != null && str.length() > 0) {
- if ((flags & PARSE_IS_EPHEMERAL) != 0) {
- outError[0] = "sharedUserId not allowed in ephemeral application";
- mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID;
- return null;
- }
String nameError = validateName(str, true, false);
if (nameError != null && !"android".equals(pkg.packageName)) {
outError[0] = "<manifest> specifies bad sharedUserId name \""
@@ -2193,10 +2050,12 @@ public class PackageParser {
com.android.internal.R.styleable.AndroidManifestResourceOverlay);
pkg.mOverlayTarget = sa.getString(
com.android.internal.R.styleable.AndroidManifestResourceOverlay_targetPackage);
+ pkg.mOverlayCategory = sa.getString(
+ com.android.internal.R.styleable.AndroidManifestResourceOverlay_category);
pkg.mOverlayPriority = sa.getInt(
com.android.internal.R.styleable.AndroidManifestResourceOverlay_priority,
0);
- pkg.mIsStaticOverlay = sa.getBoolean(
+ pkg.mOverlayIsStatic = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestResourceOverlay_isStatic,
false);
final String propName = sa.getString(
@@ -2373,8 +2232,9 @@ public class PackageParser {
return null;
}
+ boolean defaultToCurrentDevBranch = (flags & PARSE_FORCE_SDK) != 0;
final int targetSdkVersion = PackageParser.computeTargetSdkVersion(targetVers,
- targetCode, SDK_VERSION, SDK_CODENAMES, outError);
+ targetCode, SDK_CODENAMES, outError, defaultToCurrentDevBranch);
if (targetSdkVersion < 0) {
mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
return null;
@@ -2436,7 +2296,7 @@ public class PackageParser {
sa.recycle();
- if (name != null && (flags&PARSE_IS_SYSTEM) != 0) {
+ if (name != null) {
if (pkg.protectedBroadcasts == null) {
pkg.protectedBroadcasts = new ArrayList<String>();
}
@@ -2690,19 +2550,19 @@ public class PackageParser {
* application manifest, or 0 otherwise
* @param targetCode targetSdkVersion code, if specified in the application
* manifest, or {@code null} otherwise
- * @param platformSdkVersion platform SDK version number, typically
- * Build.VERSION.SDK_INT
* @param platformSdkCodenames array of allowed pre-release SDK codenames
* for this platform
* @param outError output array to populate with error, if applicable
+ * @param forceCurrentDev if development target code is not available, use the current
+ * development version by default.
* @return the targetSdkVersion to use at runtime, or -1 if the package is
* not compatible with this platform
* @hide Exposed for unit testing only.
*/
@TestApi
public static int computeTargetSdkVersion(@IntRange(from = 0) int targetVers,
- @Nullable String targetCode, @IntRange(from = 1) int platformSdkVersion,
- @NonNull String[] platformSdkCodenames, @NonNull String[] outError) {
+ @Nullable String targetCode, @NonNull String[] platformSdkCodenames,
+ @NonNull String[] outError, boolean forceCurrentDev) {
// If it's a release SDK, return the version number unmodified.
if (targetCode == null) {
return targetVers;
@@ -2710,7 +2570,7 @@ public class PackageParser {
// If it's a pre-release SDK and the codename matches this platform, it
// definitely targets this SDK.
- if (ArrayUtils.contains(platformSdkCodenames, targetCode)) {
+ if (ArrayUtils.contains(platformSdkCodenames, targetCode) || forceCurrentDev) {
return Build.VERSION_CODES.CUR_DEVELOPMENT;
}
@@ -2859,7 +2719,7 @@ public class PackageParser {
// Fot apps targeting O-MR1 we require explicit enumeration of all certs.
String[] additionalCertSha256Digests = EmptyArray.STRING;
- if (pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.O) {
+ if (pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.O_MR1) {
additionalCertSha256Digests = parseAdditionalCertificates(res, parser, outError);
if (additionalCertSha256Digests == null) {
return false;
@@ -2874,7 +2734,7 @@ public class PackageParser {
1, additionalCertSha256Digests.length);
pkg.usesStaticLibraries = ArrayUtils.add(pkg.usesStaticLibraries, lname);
- pkg.usesStaticLibrariesVersions = ArrayUtils.appendInt(
+ pkg.usesStaticLibrariesVersions = ArrayUtils.appendLong(
pkg.usesStaticLibrariesVersions, version, true);
pkg.usesStaticLibrariesCertDigests = ArrayUtils.appendElement(String[].class,
pkg.usesStaticLibrariesCertDigests, certSha256Digests, true);
@@ -3231,13 +3091,12 @@ public class PackageParser {
perm.info.descriptionRes = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestPermissionGroup_description,
0);
+ perm.info.requestRes = sa.getResourceId(
+ com.android.internal.R.styleable.AndroidManifestPermissionGroup_request, 0);
perm.info.flags = sa.getInt(
com.android.internal.R.styleable.AndroidManifestPermissionGroup_permissionGroupFlags, 0);
perm.info.priority = sa.getInt(
com.android.internal.R.styleable.AndroidManifestPermissionGroup_priority, 0);
- if (perm.info.priority > 0 && (flags&PARSE_IS_SYSTEM) == 0) {
- perm.info.priority = 0;
- }
sa.recycle();
@@ -3285,6 +3144,9 @@ public class PackageParser {
com.android.internal.R.styleable.AndroidManifestPermission_description,
0);
+ perm.info.requestRes = sa.getResourceId(
+ com.android.internal.R.styleable.AndroidManifestPermission_request, 0);
+
perm.info.protectionLevel = sa.getInt(
com.android.internal.R.styleable.AndroidManifestPermission_protectionLevel,
PermissionInfo.PROTECTION_NORMAL);
@@ -3302,7 +3164,7 @@ public class PackageParser {
perm.info.protectionLevel = PermissionInfo.fixProtectionLevel(perm.info.protectionLevel);
- if ((perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_FLAGS) != 0) {
+ if (perm.info.getProtectionFlags() != 0) {
if ( (perm.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_INSTANT) == 0
&& (perm.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) == 0
&& (perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE) !=
@@ -3359,6 +3221,7 @@ public class PackageParser {
}
perm.info.descriptionRes = 0;
+ perm.info.requestRes = 0;
perm.info.protectionLevel = PermissionInfo.PROTECTION_NORMAL;
perm.tree = true;
@@ -3539,17 +3402,14 @@ public class PackageParser {
ai.descriptionRes = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestApplication_description, 0);
- if ((flags&PARSE_IS_SYSTEM) != 0) {
- if (sa.getBoolean(
- com.android.internal.R.styleable.AndroidManifestApplication_persistent,
- false)) {
- // Check if persistence is based on a feature being present
- final String requiredFeature = sa.getNonResourceString(
- com.android.internal.R.styleable.
- AndroidManifestApplication_persistentWhenFeatureAvailable);
- if (requiredFeature == null || mCallback.hasFeature(requiredFeature)) {
- ai.flags |= ApplicationInfo.FLAG_PERSISTENT;
- }
+ if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestApplication_persistent,
+ false)) {
+ // Check if persistence is based on a feature being present
+ final String requiredFeature = sa.getNonResourceString(com.android.internal.R.styleable
+ .AndroidManifestApplication_persistentWhenFeatureAvailable);
+ if (requiredFeature == null || mCallback.hasFeature(requiredFeature)) {
+ ai.flags |= ApplicationInfo.FLAG_PERSISTENT;
}
}
@@ -3625,7 +3485,7 @@ public class PackageParser {
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_usesCleartextTraffic,
- true)) {
+ owner.applicationInfo.targetSdkVersion < Build.VERSION_CODES.P)) {
ai.flags |= ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC;
}
@@ -3695,6 +3555,11 @@ public class PackageParser {
}
ai.taskAffinity = buildTaskAffinityName(ai.packageName, ai.packageName,
str, outError);
+ String factory = sa.getNonResourceString(
+ com.android.internal.R.styleable.AndroidManifestApplication_appComponentFactory);
+ if (factory != null) {
+ ai.appComponentFactory = buildClassName(ai.packageName, factory, outError);
+ }
if (outError[0] == null) {
CharSequence pname;
@@ -3720,17 +3585,15 @@ public class PackageParser {
ai.flags |= ApplicationInfo.FLAG_IS_GAME;
}
- if (false) {
- if (sa.getBoolean(
- com.android.internal.R.styleable.AndroidManifestApplication_cantSaveState,
- false)) {
- ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE;
+ if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestApplication_cantSaveState,
+ false)) {
+ ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE;
- // A heavy-weight application can not be in a custom process.
- // We can do direct compare because we intern all strings.
- if (ai.processName != null && ai.processName != ai.packageName) {
- outError[0] = "cantSaveState applications can not use custom processes";
- }
+ // A heavy-weight application can not be in a custom process.
+ // We can do direct compare because we intern all strings.
+ if (ai.processName != null && !ai.processName.equals(ai.packageName)) {
+ outError[0] = "cantSaveState applications can not use custom processes";
}
}
}
@@ -3757,7 +3620,9 @@ public class PackageParser {
// getting added to the wrong package.
final CachedComponentArgs cachedArgs = new CachedComponentArgs();
int type;
-
+ boolean hasActivityOrder = false;
+ boolean hasReceiverOrder = false;
+ boolean hasServiceOrder = false;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
@@ -3773,6 +3638,7 @@ public class PackageParser {
return false;
}
+ hasActivityOrder |= (a.order != 0);
owner.activities.add(a);
} else if (tagName.equals("receiver")) {
@@ -3783,6 +3649,7 @@ public class PackageParser {
return false;
}
+ hasReceiverOrder |= (a.order != 0);
owner.receivers.add(a);
} else if (tagName.equals("service")) {
@@ -3792,6 +3659,7 @@ public class PackageParser {
return false;
}
+ hasServiceOrder |= (s.order != 0);
owner.services.add(s);
} else if (tagName.equals("provider")) {
@@ -3810,6 +3678,7 @@ public class PackageParser {
return false;
}
+ hasActivityOrder |= (a.order != 0);
owner.activities.add(a);
} else if (parser.getName().equals("meta-data")) {
@@ -3831,6 +3700,9 @@ public class PackageParser {
com.android.internal.R.styleable.AndroidManifestStaticLibrary_name);
final int version = sa.getInt(
com.android.internal.R.styleable.AndroidManifestStaticLibrary_version, -1);
+ final int versionMajor = sa.getInt(
+ com.android.internal.R.styleable.AndroidManifestStaticLibrary_versionMajor,
+ 0);
sa.recycle();
@@ -3858,7 +3730,12 @@ public class PackageParser {
}
owner.staticSharedLibName = lname.intern();
- owner.staticSharedLibVersion = version;
+ if (version >= 0) {
+ owner.staticSharedLibVersion =
+ PackageInfo.composeLongVersionCode(versionMajor, version);
+ } else {
+ owner.staticSharedLibVersion = version;
+ }
ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY;
XmlUtils.skipCurrentTag(parser);
@@ -3935,6 +3812,15 @@ public class PackageParser {
}
}
+ if (hasActivityOrder) {
+ Collections.sort(owner.activities, (a1, a2) -> Integer.compare(a2.order, a1.order));
+ }
+ if (hasReceiverOrder) {
+ Collections.sort(owner.receivers, (r1, r2) -> Integer.compare(r2.order, r1.order));
+ }
+ if (hasServiceOrder) {
+ Collections.sort(owner.services, (s1, s2) -> Integer.compare(s2.order, s1.order));
+ }
// Must be ran after the entire {@link ApplicationInfo} has been fully processed and after
// every activity info has had a chance to set it from its attributes.
setMaxAspectRatio(owner);
@@ -4421,13 +4307,6 @@ public class PackageParser {
if (sa.getBoolean(R.styleable.AndroidManifestActivity_singleUser, false)) {
a.info.flags |= ActivityInfo.FLAG_SINGLE_USER;
- if (a.info.exported && (flags & PARSE_IS_PRIVILEGED) == 0) {
- Slog.w(TAG, "Activity exported request ignored due to singleUser: "
- + a.className + " at " + mArchiveSourcePath + " "
- + parser.getPositionDescription());
- a.info.exported = false;
- setExported = true;
- }
}
a.info.encryptionAware = a.info.directBootAware = sa.getBoolean(
@@ -4483,6 +4362,7 @@ public class PackageParser {
+ mArchiveSourcePath + " "
+ parser.getPositionDescription());
} else {
+ a.order = Math.max(intent.getOrder(), a.order);
a.intents.add(intent);
}
// adjust activity flags when we implicitly expose it via a browsable filter
@@ -4544,24 +4424,6 @@ public class PackageParser {
outError)) == null) {
return null;
}
- // we don't have an attribute [or it's false], but, we have meta-data
- if (!visibleToEphemeral && a.metaData.getBoolean(META_DATA_INSTANT_APPS)) {
- visibleToEphemeral = true; // set in case there are more intent filters
- a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
- a.info.flags &= ~ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP;
- owner.visibleToInstantApps = true;
- // cycle through any filters already seen
- for (int i = a.intents.size() - 1; i >= 0; --i) {
- a.intents.get(i)
- .setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
- }
- if (owner.preferredActivityFilters != null) {
- for (int i = owner.preferredActivityFilters.size() - 1; i >= 0; --i) {
- owner.preferredActivityFilters.get(i)
- .setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
- }
- }
- }
} else if (!receiver && parser.getName().equals("layout")) {
parseLayout(res, parser, a);
} else {
@@ -4811,6 +4673,7 @@ public class PackageParser {
info.windowLayout = target.info.windowLayout;
info.resizeMode = target.info.resizeMode;
info.maxAspectRatio = target.info.maxAspectRatio;
+ info.requestedVrComponent = target.info.requestedVrComponent;
info.encryptionAware = info.directBootAware = target.info.directBootAware;
@@ -4878,6 +4741,7 @@ public class PackageParser {
+ mArchiveSourcePath + " "
+ parser.getPositionDescription());
} else {
+ a.order = Math.max(intent.getOrder(), a.order);
a.intents.add(intent);
}
// adjust activity flags when we implicitly expose it via a browsable filter
@@ -5016,12 +4880,6 @@ public class PackageParser {
com.android.internal.R.styleable.AndroidManifestProvider_singleUser,
false)) {
p.info.flags |= ProviderInfo.FLAG_SINGLE_USER;
- if (p.info.exported && (flags & PARSE_IS_PRIVILEGED) == 0) {
- Slog.w(TAG, "Provider exported request ignored due to singleUser: "
- + p.className + " at " + mArchiveSourcePath + " "
- + parser.getPositionDescription());
- p.info.exported = false;
- }
}
p.info.encryptionAware = p.info.directBootAware = sa.getBoolean(
@@ -5062,7 +4920,7 @@ public class PackageParser {
p.info.authority = cpname.intern();
if (!parseProviderTags(
- res, parser, visibleToEphemeral, owner, p, outError)) {
+ res, parser, visibleToEphemeral, p, outError)) {
return null;
}
@@ -5070,7 +4928,7 @@ public class PackageParser {
}
private boolean parseProviderTags(Resources res, XmlResourceParser parser,
- boolean visibleToEphemeral, Package owner, Provider outInfo, String[] outError)
+ boolean visibleToEphemeral, Provider outInfo, String[] outError)
throws XmlPullParserException, IOException {
int outerDepth = parser.getDepth();
int type;
@@ -5091,6 +4949,7 @@ public class PackageParser {
intent.setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
outInfo.info.flags |= ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP;
}
+ outInfo.order = Math.max(intent.getOrder(), outInfo.order);
outInfo.intents.add(intent);
} else if (parser.getName().equals("meta-data")) {
@@ -5098,17 +4957,6 @@ public class PackageParser {
outInfo.metaData, outError)) == null) {
return false;
}
- // we don't have an attribute [or it's false], but, we have meta-data
- if (!visibleToEphemeral && outInfo.metaData.getBoolean(META_DATA_INSTANT_APPS)) {
- visibleToEphemeral = true; // set in case there are more intent filters
- outInfo.info.flags |= ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP;
- owner.visibleToInstantApps = true;
- // cycle through any filters already seen
- for (int i = outInfo.intents.size() - 1; i >= 0; --i) {
- outInfo.intents.get(i)
- .setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
- }
- }
} else if (parser.getName().equals("grant-uri-permission")) {
TypedArray sa = res.obtainAttributes(parser,
@@ -5343,13 +5191,6 @@ public class PackageParser {
com.android.internal.R.styleable.AndroidManifestService_singleUser,
false)) {
s.info.flags |= ServiceInfo.FLAG_SINGLE_USER;
- if (s.info.exported && (flags & PARSE_IS_PRIVILEGED) == 0) {
- Slog.w(TAG, "Service exported request ignored due to singleUser: "
- + s.className + " at " + mArchiveSourcePath + " "
- + parser.getPositionDescription());
- s.info.exported = false;
- setExported = true;
- }
}
s.info.encryptionAware = s.info.directBootAware = sa.getBoolean(
@@ -5398,23 +5239,13 @@ public class PackageParser {
intent.setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
s.info.flags |= ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP;
}
+ s.order = Math.max(intent.getOrder(), s.order);
s.intents.add(intent);
} else if (parser.getName().equals("meta-data")) {
if ((s.metaData=parseMetaData(res, parser, s.metaData,
outError)) == null) {
return null;
}
- // we don't have an attribute [or it's false], but, we have meta-data
- if (!visibleToEphemeral && s.metaData.getBoolean(META_DATA_INSTANT_APPS)) {
- visibleToEphemeral = true; // set in case there are more intent filters
- s.info.flags |= ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP;
- owner.visibleToInstantApps = true;
- // cycle through any filters already seen
- for (int i = s.intents.size() - 1; i >= 0; --i) {
- s.intents.get(i)
- .setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
- }
- }
} else {
if (!RIGID_PARSER) {
Slog.w(TAG, "Unknown element under <service>: "
@@ -5634,6 +5465,10 @@ public class PackageParser {
com.android.internal.R.styleable.AndroidManifestIntentFilter_priority, 0);
outInfo.setPriority(priority);
+ int order = sa.getInt(
+ com.android.internal.R.styleable.AndroidManifestIntentFilter_order, 0);
+ outInfo.setOrder(order);
+
TypedValue v = sa.peekValue(
com.android.internal.R.styleable.AndroidManifestIntentFilter_label);
if (v != null && (outInfo.labelRes=v.resourceId) == 0) {
@@ -5813,6 +5648,548 @@ public class PackageParser {
}
/**
+ * A container for signing-related data of an application package.
+ * @hide
+ */
+ public static final class SigningDetails implements Parcelable {
+
+ @IntDef({SigningDetails.SignatureSchemeVersion.UNKNOWN,
+ SigningDetails.SignatureSchemeVersion.JAR,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3})
+ public @interface SignatureSchemeVersion {
+ int UNKNOWN = 0;
+ int JAR = 1;
+ int SIGNING_BLOCK_V2 = 2;
+ int SIGNING_BLOCK_V3 = 3;
+ }
+
+ @Nullable
+ public final Signature[] signatures;
+ @SignatureSchemeVersion
+ public final int signatureSchemeVersion;
+ @Nullable
+ public final ArraySet<PublicKey> publicKeys;
+
+ /**
+ * APK Signature Scheme v3 includes support for adding a proof-of-rotation record that
+ * contains two pieces of information:
+ * 1) the past signing certificates
+ * 2) the flags that APK wants to assign to each of the past signing certificates.
+ *
+ * This collection of {@code Signature} objects, each of which is formed from a former
+ * signing certificate of this APK before it was changed by signing certificate rotation,
+ * represents the first piece of information. It is the APK saying to the rest of the
+ * world: "hey if you trust the old cert, you can trust me!" This is useful, if for
+ * instance, the platform would like to determine whether or not to allow this APK to do
+ * something it would've allowed it to do under the old cert (like upgrade).
+ */
+ @Nullable
+ public final Signature[] pastSigningCertificates;
+
+ /** special value used to see if cert is in package - not exposed to callers */
+ private static final int PAST_CERT_EXISTS = 0;
+
+ @IntDef(
+ flag = true,
+ value = {CertCapabilities.INSTALLED_DATA,
+ CertCapabilities.SHARED_USER_ID,
+ CertCapabilities.PERMISSION,
+ CertCapabilities.ROLLBACK})
+ public @interface CertCapabilities {
+
+ /** accept data from already installed pkg with this cert */
+ int INSTALLED_DATA = 1;
+
+ /** accept sharedUserId with pkg with this cert */
+ int SHARED_USER_ID = 2;
+
+ /** grant SIGNATURE permissions to pkgs with this cert */
+ int PERMISSION = 4;
+
+ /** allow pkg to update to one signed by this certificate */
+ int ROLLBACK = 8;
+
+ /** allow pkg to continue to have auth access gated by this cert */
+ int AUTH = 16;
+ }
+
+ /**
+ * APK Signature Scheme v3 includes support for adding a proof-of-rotation record that
+ * contains two pieces of information:
+ * 1) the past signing certificates
+ * 2) the flags that APK wants to assign to each of the past signing certificates.
+ *
+ * These flags, which have a one-to-one relationship for the {@code pastSigningCertificates}
+ * collection, represent the second piece of information and are viewed as capabilities.
+ * They are an APK's way of telling the platform: "this is how I want to trust my old certs,
+ * please enforce that." This is useful for situation where this app itself is using its
+ * signing certificate as an authorization mechanism, like whether or not to allow another
+ * app to have its SIGNATURE permission. An app could specify whether to allow other apps
+ * signed by its old cert 'X' to still get a signature permission it defines, for example.
+ */
+ @Nullable
+ public final int[] pastSigningCertificatesFlags;
+
+ /** A representation of unknown signing details. Use instead of null. */
+ public static final SigningDetails UNKNOWN =
+ new SigningDetails(null, SignatureSchemeVersion.UNKNOWN, null, null, null);
+
+ @VisibleForTesting
+ public SigningDetails(Signature[] signatures,
+ @SignatureSchemeVersion int signatureSchemeVersion,
+ ArraySet<PublicKey> keys, Signature[] pastSigningCertificates,
+ int[] pastSigningCertificatesFlags) {
+ this.signatures = signatures;
+ this.signatureSchemeVersion = signatureSchemeVersion;
+ this.publicKeys = keys;
+ this.pastSigningCertificates = pastSigningCertificates;
+ this.pastSigningCertificatesFlags = pastSigningCertificatesFlags;
+ }
+
+ public SigningDetails(Signature[] signatures,
+ @SignatureSchemeVersion int signatureSchemeVersion,
+ Signature[] pastSigningCertificates, int[] pastSigningCertificatesFlags)
+ throws CertificateException {
+ this(signatures, signatureSchemeVersion, toSigningKeys(signatures),
+ pastSigningCertificates, pastSigningCertificatesFlags);
+ }
+
+ public SigningDetails(Signature[] signatures,
+ @SignatureSchemeVersion int signatureSchemeVersion)
+ throws CertificateException {
+ this(signatures, signatureSchemeVersion,
+ null, null);
+ }
+
+ public SigningDetails(SigningDetails orig) {
+ if (orig != null) {
+ if (orig.signatures != null) {
+ this.signatures = orig.signatures.clone();
+ } else {
+ this.signatures = null;
+ }
+ this.signatureSchemeVersion = orig.signatureSchemeVersion;
+ this.publicKeys = new ArraySet<>(orig.publicKeys);
+ if (orig.pastSigningCertificates != null) {
+ this.pastSigningCertificates = orig.pastSigningCertificates.clone();
+ this.pastSigningCertificatesFlags = orig.pastSigningCertificatesFlags.clone();
+ } else {
+ this.pastSigningCertificates = null;
+ this.pastSigningCertificatesFlags = null;
+ }
+ } else {
+ this.signatures = null;
+ this.signatureSchemeVersion = SignatureSchemeVersion.UNKNOWN;
+ this.publicKeys = null;
+ this.pastSigningCertificates = null;
+ this.pastSigningCertificatesFlags = null;
+ }
+ }
+
+ /** Returns true if the signing details have one or more signatures. */
+ public boolean hasSignatures() {
+ return signatures != null && signatures.length > 0;
+ }
+
+ /** Returns true if the signing details have past signing certificates. */
+ public boolean hasPastSigningCertificates() {
+ return pastSigningCertificates != null && pastSigningCertificates.length > 0;
+ }
+
+ /**
+ * Determines if the provided {@code oldDetails} is an ancestor of or the same as this one.
+ * If the {@code oldDetails} signing certificate appears in our pastSigningCertificates,
+ * then that means it has authorized a signing certificate rotation, which eventually leads
+ * to our certificate, and thus can be trusted. If this method evaluates to true, this
+ * SigningDetails object should be trusted if the previous one is.
+ */
+ public boolean hasAncestorOrSelf(SigningDetails oldDetails) {
+ if (this == UNKNOWN || oldDetails == UNKNOWN) {
+ return false;
+ }
+ if (oldDetails.signatures.length > 1) {
+
+ // multiple-signer packages cannot rotate signing certs, so we just compare current
+ // signers for an exact match
+ return signaturesMatchExactly(oldDetails);
+ } else {
+
+ // we may have signing certificate rotation history, check to see if the oldDetails
+ // was one of our old signing certificates
+ return hasCertificate(oldDetails.signatures[0]);
+ }
+ }
+
+ /**
+ * Similar to {@code hasAncestorOrSelf}. Returns true only if this {@code SigningDetails}
+ * is a descendant of {@code oldDetails}, not if they're the same. This is used to
+ * determine if this object is newer than the provided one.
+ */
+ public boolean hasAncestor(SigningDetails oldDetails) {
+ if (this == UNKNOWN || oldDetails == UNKNOWN) {
+ return false;
+ }
+ if (this.hasPastSigningCertificates() && oldDetails.signatures.length == 1) {
+
+ // the last entry in pastSigningCertificates is the current signer, ignore it
+ for (int i = 0; i < pastSigningCertificates.length - 1; i++) {
+ if (pastSigningCertificates[i].equals(oldDetails.signatures[i])) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Determines if the provided {@code oldDetails} is an ancestor of this one, and whether or
+ * not this one grants it the provided capability, represented by the {@code flags}
+ * parameter. In the event of signing certificate rotation, a package may still interact
+ * with entities signed by its old signing certificate and not want to break previously
+ * functioning behavior. The {@code flags} value determines which capabilities the app
+ * signed by the newer signing certificate would like to continue to give to its previous
+ * signing certificate(s).
+ */
+ public boolean checkCapability(SigningDetails oldDetails, @CertCapabilities int flags) {
+ if (this == UNKNOWN || oldDetails == UNKNOWN) {
+ return false;
+ }
+ if (oldDetails.signatures.length > 1) {
+
+ // multiple-signer packages cannot rotate signing certs, so we must have an exact
+ // match, which also means all capabilities are granted
+ return signaturesMatchExactly(oldDetails);
+ } else {
+
+ // we may have signing certificate rotation history, check to see if the oldDetails
+ // was one of our old signing certificates, and if we grant it the capability it's
+ // requesting
+ return hasCertificate(oldDetails.signatures[0], flags);
+ }
+ }
+
+ /**
+ * A special case of {@code checkCapability} which re-encodes both sets of signing
+ * certificates to counteract a previous re-encoding.
+ */
+ public boolean checkCapabilityRecover(SigningDetails oldDetails,
+ @CertCapabilities int flags) throws CertificateException {
+ if (oldDetails == UNKNOWN || this == UNKNOWN) {
+ return false;
+ }
+ if (hasPastSigningCertificates() && oldDetails.signatures.length == 1) {
+
+ // signing certificates may have rotated, check entire history for effective match
+ for (int i = 0; i < pastSigningCertificates.length; i++) {
+ if (Signature.areEffectiveMatch(
+ oldDetails.signatures[0],
+ pastSigningCertificates[i])
+ && pastSigningCertificatesFlags[i] == flags) {
+ return true;
+ }
+ }
+ } else {
+ return Signature.areEffectiveMatch(oldDetails.signatures, signatures);
+ }
+ return false;
+ }
+
+ /**
+ * Determine if {@code signature} is in this SigningDetails' signing certificate history,
+ * including the current signer. Automatically returns false if this object has multiple
+ * signing certificates, since rotation is only supported for single-signers; this is
+ * enforced by {@code hasCertificateInternal}.
+ */
+ public boolean hasCertificate(Signature signature) {
+ return hasCertificateInternal(signature, PAST_CERT_EXISTS);
+ }
+
+ /**
+ * Determine if {@code signature} is in this SigningDetails' signing certificate history,
+ * including the current signer, and whether or not it has the given permission.
+ * Certificates which match our current signer automatically get all capabilities.
+ * Automatically returns false if this object has multiple signing certificates, since
+ * rotation is only supported for single-signers.
+ */
+ public boolean hasCertificate(Signature signature, @CertCapabilities int flags) {
+ return hasCertificateInternal(signature, flags);
+ }
+
+ /** Convenient wrapper for calling {@code hasCertificate} with certificate's raw bytes. */
+ public boolean hasCertificate(byte[] certificate) {
+ Signature signature = new Signature(certificate);
+ return hasCertificate(signature);
+ }
+
+ private boolean hasCertificateInternal(Signature signature, int flags) {
+ if (this == UNKNOWN) {
+ return false;
+ }
+
+ // only single-signed apps can have pastSigningCertificates
+ if (hasPastSigningCertificates()) {
+
+ // check all past certs, except for the current one, which automatically gets all
+ // capabilities, since it is the same as the current signature
+ for (int i = 0; i < pastSigningCertificates.length - 1; i++) {
+ if (pastSigningCertificates[i].equals(signature)) {
+ if (flags == PAST_CERT_EXISTS
+ || (flags & pastSigningCertificatesFlags[i]) == flags) {
+ return true;
+ }
+ }
+ }
+ }
+
+ // not in previous certs signing history, just check the current signer and make sure
+ // we are singly-signed
+ return signatures.length == 1 && signatures[0].equals(signature);
+ }
+
+ /**
+ * Determines if the provided {@code sha256String} is an ancestor of this one, and whether
+ * or not this one grants it the provided capability, represented by the {@code flags}
+ * parameter. In the event of signing certificate rotation, a package may still interact
+ * with entities signed by its old signing certificate and not want to break previously
+ * functioning behavior. The {@code flags} value determines which capabilities the app
+ * signed by the newer signing certificate would like to continue to give to its previous
+ * signing certificate(s).
+ *
+ * @param sha256String A hex-encoded representation of a sha256 digest. In the case of an
+ * app with multiple signers, this represents the hex-encoded sha256
+ * digest of the combined hex-encoded sha256 digests of each individual
+ * signing certificate according to {@link
+ * PackageUtils#computeSignaturesSha256Digest(Signature[])}
+ */
+ public boolean checkCapability(String sha256String, @CertCapabilities int flags) {
+ if (this == UNKNOWN) {
+ return false;
+ }
+
+ // first see if the hash represents a single-signer in our signing history
+ byte[] sha256Bytes = ByteStringUtils.fromHexToByteArray(sha256String);
+ if (hasSha256Certificate(sha256Bytes, flags)) {
+ return true;
+ }
+
+ // Not in signing history, either represents multiple signatures or not a match.
+ // Multiple signers can't rotate, so no need to check flags, just see if the SHAs match.
+ // We already check the single-signer case above as part of hasSha256Certificate, so no
+ // need to verify we have multiple signers, just run the old check
+ // just consider current signing certs
+ final String[] mSignaturesSha256Digests =
+ PackageUtils.computeSignaturesSha256Digests(signatures);
+ final String mSignaturesSha256Digest =
+ PackageUtils.computeSignaturesSha256Digest(mSignaturesSha256Digests);
+ return mSignaturesSha256Digest.equals(sha256String);
+ }
+
+ /**
+ * Determine if the {@code sha256Certificate} is in this SigningDetails' signing certificate
+ * history, including the current signer. Automatically returns false if this object has
+ * multiple signing certificates, since rotation is only supported for single-signers.
+ */
+ public boolean hasSha256Certificate(byte[] sha256Certificate) {
+ return hasSha256CertificateInternal(sha256Certificate, PAST_CERT_EXISTS);
+ }
+
+ /**
+ * Determine if the {@code sha256Certificate} certificate hash corresponds to a signing
+ * certificate in this SigningDetails' signing certificate history, including the current
+ * signer, and whether or not it has the given permission. Certificates which match our
+ * current signer automatically get all capabilities. Automatically returns false if this
+ * object has multiple signing certificates, since rotation is only supported for
+ * single-signers.
+ */
+ public boolean hasSha256Certificate(byte[] sha256Certificate, @CertCapabilities int flags) {
+ return hasSha256CertificateInternal(sha256Certificate, flags);
+ }
+
+ private boolean hasSha256CertificateInternal(byte[] sha256Certificate, int flags) {
+ if (this == UNKNOWN) {
+ return false;
+ }
+ if (hasPastSigningCertificates()) {
+
+ // check all past certs, except for the last one, which automatically gets all
+ // capabilities, since it is the same as the current signature, and is checked below
+ for (int i = 0; i < pastSigningCertificates.length - 1; i++) {
+ byte[] digest = PackageUtils.computeSha256DigestBytes(
+ pastSigningCertificates[i].toByteArray());
+ if (Arrays.equals(sha256Certificate, digest)) {
+ if (flags == PAST_CERT_EXISTS
+ || (flags & pastSigningCertificatesFlags[i]) == flags) {
+ return true;
+ }
+ }
+ }
+ }
+
+ // not in previous certs signing history, just check the current signer
+ if (signatures.length == 1) {
+ byte[] digest =
+ PackageUtils.computeSha256DigestBytes(signatures[0].toByteArray());
+ return Arrays.equals(sha256Certificate, digest);
+ }
+ return false;
+ }
+
+ /** Returns true if the signatures in this and other match exactly. */
+ public boolean signaturesMatchExactly(SigningDetails other) {
+ return Signature.areExactMatch(this.signatures, other.signatures);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ boolean isUnknown = UNKNOWN == this;
+ dest.writeBoolean(isUnknown);
+ if (isUnknown) {
+ return;
+ }
+ dest.writeTypedArray(this.signatures, flags);
+ dest.writeInt(this.signatureSchemeVersion);
+ dest.writeArraySet(this.publicKeys);
+ dest.writeTypedArray(this.pastSigningCertificates, flags);
+ dest.writeIntArray(this.pastSigningCertificatesFlags);
+ }
+
+ protected SigningDetails(Parcel in) {
+ final ClassLoader boot = Object.class.getClassLoader();
+ this.signatures = in.createTypedArray(Signature.CREATOR);
+ this.signatureSchemeVersion = in.readInt();
+ this.publicKeys = (ArraySet<PublicKey>) in.readArraySet(boot);
+ this.pastSigningCertificates = in.createTypedArray(Signature.CREATOR);
+ this.pastSigningCertificatesFlags = in.createIntArray();
+ }
+
+ public static final Creator<SigningDetails> CREATOR = new Creator<SigningDetails>() {
+ @Override
+ public SigningDetails createFromParcel(Parcel source) {
+ if (source.readBoolean()) {
+ return UNKNOWN;
+ }
+ return new SigningDetails(source);
+ }
+
+ @Override
+ public SigningDetails[] newArray(int size) {
+ return new SigningDetails[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof SigningDetails)) return false;
+
+ SigningDetails that = (SigningDetails) o;
+
+ if (signatureSchemeVersion != that.signatureSchemeVersion) return false;
+ if (!Signature.areExactMatch(signatures, that.signatures)) return false;
+ if (publicKeys != null) {
+ if (!publicKeys.equals((that.publicKeys))) {
+ return false;
+ }
+ } else if (that.publicKeys != null) {
+ return false;
+ }
+
+ // can't use Signature.areExactMatch() because order matters with the past signing certs
+ if (!Arrays.equals(pastSigningCertificates, that.pastSigningCertificates)) {
+ return false;
+ }
+ if (!Arrays.equals(pastSigningCertificatesFlags, that.pastSigningCertificatesFlags)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = +Arrays.hashCode(signatures);
+ result = 31 * result + signatureSchemeVersion;
+ result = 31 * result + (publicKeys != null ? publicKeys.hashCode() : 0);
+ result = 31 * result + Arrays.hashCode(pastSigningCertificates);
+ result = 31 * result + Arrays.hashCode(pastSigningCertificatesFlags);
+ return result;
+ }
+
+ /**
+ * Builder of {@code SigningDetails} instances.
+ */
+ public static class Builder {
+ private Signature[] mSignatures;
+ private int mSignatureSchemeVersion = SignatureSchemeVersion.UNKNOWN;
+ private Signature[] mPastSigningCertificates;
+ private int[] mPastSigningCertificatesFlags;
+
+ public Builder() {
+ }
+
+ /** get signing certificates used to sign the current APK */
+ public Builder setSignatures(Signature[] signatures) {
+ mSignatures = signatures;
+ return this;
+ }
+
+ /** set the signature scheme version used to sign the APK */
+ public Builder setSignatureSchemeVersion(int signatureSchemeVersion) {
+ mSignatureSchemeVersion = signatureSchemeVersion;
+ return this;
+ }
+
+ /** set the signing certificates by which the APK proved it can be authenticated */
+ public Builder setPastSigningCertificates(Signature[] pastSigningCertificates) {
+ mPastSigningCertificates = pastSigningCertificates;
+ return this;
+ }
+
+ /** set the flags for the {@code pastSigningCertificates} */
+ public Builder setPastSigningCertificatesFlags(int[] pastSigningCertificatesFlags) {
+ mPastSigningCertificatesFlags = pastSigningCertificatesFlags;
+ return this;
+ }
+
+ private void checkInvariants() {
+ // must have signatures and scheme version set
+ if (mSignatures == null) {
+ throw new IllegalStateException("SigningDetails requires the current signing"
+ + " certificates.");
+ }
+
+ // pastSigningCerts and flags must match up
+ boolean pastMismatch = false;
+ if (mPastSigningCertificates != null && mPastSigningCertificatesFlags != null) {
+ if (mPastSigningCertificates.length != mPastSigningCertificatesFlags.length) {
+ pastMismatch = true;
+ }
+ } else if (!(mPastSigningCertificates == null
+ && mPastSigningCertificatesFlags == null)) {
+ pastMismatch = true;
+ }
+ if (pastMismatch) {
+ throw new IllegalStateException("SigningDetails must have a one to one mapping "
+ + "between pastSigningCertificates and pastSigningCertificatesFlags");
+ }
+ }
+ /** build a {@code SigningDetails} object */
+ public SigningDetails build()
+ throws CertificateException {
+ checkInvariants();
+ return new SigningDetails(mSignatures, mSignatureSchemeVersion,
+ mPastSigningCertificates, mPastSigningCertificatesFlags);
+ }
+ }
+ }
+
+ /**
* Representation of a full package parsed from APK files on disk. A package
* consists of a single base APK, and zero or more split APKs.
*/
@@ -5879,11 +6256,11 @@ public class PackageParser {
public ArrayList<Package> childPackages;
public String staticSharedLibName = null;
- public int staticSharedLibVersion = 0;
+ public long staticSharedLibVersion = 0;
public ArrayList<String> libraryNames = null;
public ArrayList<String> usesLibraries = null;
public ArrayList<String> usesStaticLibraries = null;
- public int[] usesStaticLibrariesVersions = null;
+ public long[] usesStaticLibrariesVersions = null;
public String[][] usesStaticLibrariesCertDigests = null;
public ArrayList<String> usesOptionalLibraries = null;
public String[] usesLibraryFiles = null;
@@ -5900,6 +6277,14 @@ public class PackageParser {
// The version code declared for this package.
public int mVersionCode;
+ // The major version code declared for this package.
+ public int mVersionCodeMajor;
+
+ // Return long containing mVersionCode and mVersionCodeMajor.
+ public long getLongVersionCode() {
+ return PackageInfo.composeLongVersionCode(mVersionCodeMajor, mVersionCode);
+ }
+
// The version name declared for this package.
public String mVersionName;
@@ -5910,8 +6295,7 @@ public class PackageParser {
public int mSharedUserLabel;
// Signatures that were read from the package.
- public Signature[] mSignatures;
- public Certificate[][] mCertificates;
+ @NonNull public SigningDetails mSigningDetails = SigningDetails.UNKNOWN;
// For use by package manager service for quick lookup of
// preferred up order.
@@ -5953,14 +6337,16 @@ public class PackageParser {
public String mRequiredAccountType;
public String mOverlayTarget;
+ public String mOverlayCategory;
public int mOverlayPriority;
- public boolean mIsStaticOverlay;
- public boolean mTrustedOverlay;
+ public boolean mOverlayIsStatic;
+
+ public int mCompileSdkVersion;
+ public String mCompileSdkVersionCodename;
/**
* Data used to feed the KeySetManagerService
*/
- public ArraySet<PublicKey> mSigningKeys;
public ArraySet<String> mUpgradeKeySets;
public ArrayMap<String, ArraySet<PublicKey>> mKeySetMapping;
@@ -6017,6 +6403,8 @@ public class PackageParser {
}
}
+ /** @deprecated Forward locked apps no longer supported. Resource path not needed. */
+ @Deprecated
public void setApplicationInfoResourcePath(String resourcePath) {
this.applicationInfo.setResourcePath(resourcePath);
if (childPackages != null) {
@@ -6027,6 +6415,8 @@ public class PackageParser {
}
}
+ /** @deprecated Forward locked apps no longer supported. Resource path not needed. */
+ @Deprecated
public void setApplicationInfoBaseResourcePath(String resourcePath) {
this.applicationInfo.setBaseResourcePath(resourcePath);
if (childPackages != null) {
@@ -6075,6 +6465,8 @@ public class PackageParser {
// Children have no splits
}
+ /** @deprecated Forward locked apps no longer supported. Resource path not needed. */
+ @Deprecated
public void setApplicationInfoSplitResourcePaths(String[] resroucePaths) {
this.applicationInfo.setSplitResourcePaths(resroucePaths);
// Children have no splits
@@ -6104,12 +6496,13 @@ public class PackageParser {
}
}
- public void setSignatures(Signature[] signatures) {
- this.mSignatures = signatures;
+ /** Sets signing details on the package and any of its children. */
+ public void setSigningDetails(@NonNull SigningDetails signingDetails) {
+ mSigningDetails = signingDetails;
if (childPackages != null) {
final int packageCount = childPackages.size();
for (int i = 0; i < packageCount; i++) {
- childPackages.get(i).mSignatures = signatures;
+ childPackages.get(i).mSigningDetails = signingDetails;
}
}
}
@@ -6232,48 +6625,58 @@ public class PackageParser {
return false;
}
- /**
- * @hide
- */
+ /** @hide */
+ public boolean isExternal() {
+ return applicationInfo.isExternal();
+ }
+
+ /** @hide */
public boolean isForwardLocked() {
return applicationInfo.isForwardLocked();
}
- /**
- * @hide
- */
- public boolean isSystemApp() {
- return applicationInfo.isSystemApp();
+ /** @hide */
+ public boolean isOem() {
+ return applicationInfo.isOem();
}
- /**
- * @hide
- */
- public boolean isPrivilegedApp() {
+ /** @hide */
+ public boolean isVendor() {
+ return applicationInfo.isVendor();
+ }
+
+ /** @hide */
+ public boolean isProduct() {
+ return applicationInfo.isProduct();
+ }
+
+ /** @hide */
+ public boolean isPrivileged() {
return applicationInfo.isPrivilegedApp();
}
- /**
- * @hide
- */
+ /** @hide */
+ public boolean isSystem() {
+ return applicationInfo.isSystemApp();
+ }
+
+ /** @hide */
public boolean isUpdatedSystemApp() {
return applicationInfo.isUpdatedSystemApp();
}
- /**
- * @hide
- */
+ /** @hide */
public boolean canHaveOatDir() {
// The following app types CANNOT have oat directory
// - non-updated system apps
// - forward-locked apps or apps installed in ASEC containers
- return (!isSystemApp() || isUpdatedSystemApp())
+ return (!isSystem() || isUpdatedSystemApp())
&& !isForwardLocked() && !applicationInfo.isExternalAsec();
}
public boolean isMatch(int flags) {
if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) {
- return isSystemApp();
+ return isSystem();
}
return true;
}
@@ -6366,7 +6769,7 @@ public class PackageParser {
if (staticSharedLibName != null) {
staticSharedLibName = staticSharedLibName.intern();
}
- staticSharedLibVersion = dest.readInt();
+ staticSharedLibVersion = dest.readLong();
libraryNames = dest.createStringArrayList();
internStringArrayList(libraryNames);
usesLibraries = dest.createStringArrayList();
@@ -6380,8 +6783,8 @@ public class PackageParser {
usesStaticLibraries = new ArrayList<>(libCount);
dest.readStringList(usesStaticLibraries);
internStringArrayList(usesStaticLibraries);
- usesStaticLibrariesVersions = new int[libCount];
- dest.readIntArray(usesStaticLibrariesVersions);
+ usesStaticLibrariesVersions = new long[libCount];
+ dest.readLongArray(usesStaticLibrariesVersions);
usesStaticLibrariesCertDigests = new String[libCount][];
for (int i = 0; i < libCount; i++) {
usesStaticLibrariesCertDigests[i] = dest.createStringArray();
@@ -6399,6 +6802,7 @@ public class PackageParser {
mAdoptPermissions = dest.createStringArrayList();
mAppMetaData = dest.readBundle();
mVersionCode = dest.readInt();
+ mVersionCodeMajor = dest.readInt();
mVersionName = dest.readString();
if (mVersionName != null) {
mVersionName = mVersionName.intern();
@@ -6409,8 +6813,7 @@ public class PackageParser {
}
mSharedUserLabel = dest.readInt();
- mSignatures = (Signature[]) dest.readParcelableArray(boot, Signature.class);
- mCertificates = (Certificate[][]) dest.readSerializable();
+ mSigningDetails = dest.readParcelable(boot);
mPreferredOrder = dest.readInt();
@@ -6445,10 +6848,11 @@ public class PackageParser {
mRestrictedAccountType = dest.readString();
mRequiredAccountType = dest.readString();
mOverlayTarget = dest.readString();
+ mOverlayCategory = dest.readString();
mOverlayPriority = dest.readInt();
- mIsStaticOverlay = (dest.readInt() == 1);
- mTrustedOverlay = (dest.readInt() == 1);
- mSigningKeys = (ArraySet<PublicKey>) dest.readArraySet(boot);
+ mOverlayIsStatic = (dest.readInt() == 1);
+ mCompileSdkVersion = dest.readInt();
+ mCompileSdkVersionCodename = dest.readString();
mUpgradeKeySets = (ArraySet<String>) dest.readArraySet(boot);
mKeySetMapping = readKeySetMapping(dest);
@@ -6519,7 +6923,7 @@ public class PackageParser {
dest.writeParcelableList(childPackages, flags);
dest.writeString(staticSharedLibName);
- dest.writeInt(staticSharedLibVersion);
+ dest.writeLong(staticSharedLibVersion);
dest.writeStringList(libraryNames);
dest.writeStringList(usesLibraries);
dest.writeStringList(usesOptionalLibraries);
@@ -6530,7 +6934,7 @@ public class PackageParser {
} else {
dest.writeInt(usesStaticLibraries.size());
dest.writeStringList(usesStaticLibraries);
- dest.writeIntArray(usesStaticLibrariesVersions);
+ dest.writeLongArray(usesStaticLibrariesVersions);
for (String[] usesStaticLibrariesCertDigest : usesStaticLibrariesCertDigests) {
dest.writeStringArray(usesStaticLibrariesCertDigest);
}
@@ -6543,12 +6947,12 @@ public class PackageParser {
dest.writeStringList(mAdoptPermissions);
dest.writeBundle(mAppMetaData);
dest.writeInt(mVersionCode);
+ dest.writeInt(mVersionCodeMajor);
dest.writeString(mVersionName);
dest.writeString(mSharedUserId);
dest.writeInt(mSharedUserLabel);
- dest.writeParcelableArray(mSignatures, flags);
- dest.writeSerializable(mCertificates);
+ dest.writeParcelable(mSigningDetails, flags);
dest.writeInt(mPreferredOrder);
@@ -6568,10 +6972,11 @@ public class PackageParser {
dest.writeString(mRestrictedAccountType);
dest.writeString(mRequiredAccountType);
dest.writeString(mOverlayTarget);
+ dest.writeString(mOverlayCategory);
dest.writeInt(mOverlayPriority);
- dest.writeInt(mIsStaticOverlay ? 1 : 0);
- dest.writeInt(mTrustedOverlay ? 1 : 0);
- dest.writeArraySet(mSigningKeys);
+ dest.writeInt(mOverlayIsStatic ? 1 : 0);
+ dest.writeInt(mCompileSdkVersion);
+ dest.writeString(mCompileSdkVersionCodename);
dest.writeArraySet(mUpgradeKeySets);
writeKeySetMapping(dest, mKeySetMapping);
dest.writeString(cpuAbiOverride);
@@ -6658,6 +7063,8 @@ public class PackageParser {
public Bundle metaData;
public Package owner;
+ /** The order of this component in relation to its peers */
+ public int order;
ComponentName componentName;
String componentShortName;
@@ -6858,6 +7265,11 @@ public class PackageParser {
dest.writeParcelable(group, flags);
}
+ /** @hide */
+ public boolean isAppOp() {
+ return info.isAppOp();
+ }
+
private Permission(Parcel in) {
super(in);
final ClassLoader boot = Object.class.getClassLoader();
@@ -7171,6 +7583,7 @@ public class PackageParser {
for (ActivityIntentInfo aii : intents) {
aii.activity = this;
+ order = Math.max(aii.getOrder(), order);
}
if (info.permission != null) {
@@ -7260,6 +7673,7 @@ public class PackageParser {
for (ServiceIntentInfo aii : intents) {
aii.service = this;
+ order = Math.max(aii.getOrder(), order);
}
if (info.permission != null) {
@@ -7573,33 +7987,6 @@ public class PackageParser {
sCompatibilityModeEnabled = compatibilityModeEnabled;
}
- private static AtomicReference<byte[]> sBuffer = new AtomicReference<byte[]>();
-
- public static long readFullyIgnoringContents(InputStream in) throws IOException {
- byte[] buffer = sBuffer.getAndSet(null);
- if (buffer == null) {
- buffer = new byte[4096];
- }
-
- int n = 0;
- int count = 0;
- while ((n = in.read(buffer, 0, buffer.length)) != -1) {
- count += n;
- }
-
- sBuffer.set(buffer);
- return count;
- }
-
- public static void closeQuietly(StrictJarFile jarFile) {
- if (jarFile != null) {
- try {
- jarFile.close();
- } catch (Exception ignored) {
- }
- }
- }
-
public static class PackageParserException extends Exception {
public final int error;
diff --git a/core/java/android/content/pm/PackageSharedLibraryUpdater.java b/core/java/android/content/pm/PackageSharedLibraryUpdater.java
new file mode 100644
index 000000000000..b14b321a2481
--- /dev/null
+++ b/core/java/android/content/pm/PackageSharedLibraryUpdater.java
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+package android.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Build;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+
+import java.util.ArrayList;
+
+/**
+ * Base for classes that update a {@link PackageParser.Package}'s shared libraries.
+ *
+ * @hide
+ */
+@VisibleForTesting
+public abstract class PackageSharedLibraryUpdater {
+
+ /**
+ * Update the package's shared libraries.
+ *
+ * @param pkg the package to update.
+ */
+ public abstract void updatePackage(PackageParser.Package pkg);
+
+ static void removeLibrary(PackageParser.Package pkg, String libraryName) {
+ pkg.usesLibraries = ArrayUtils.remove(pkg.usesLibraries, libraryName);
+ pkg.usesOptionalLibraries =
+ ArrayUtils.remove(pkg.usesOptionalLibraries, libraryName);
+ }
+
+ static @NonNull
+ <T> ArrayList<T> prefix(@Nullable ArrayList<T> cur, T val) {
+ if (cur == null) {
+ cur = new ArrayList<>();
+ }
+ cur.add(0, val);
+ return cur;
+ }
+
+ private static boolean isLibraryPresent(ArrayList<String> usesLibraries,
+ ArrayList<String> usesOptionalLibraries, String apacheHttpLegacy) {
+ return ArrayUtils.contains(usesLibraries, apacheHttpLegacy)
+ || ArrayUtils.contains(usesOptionalLibraries, apacheHttpLegacy);
+ }
+
+ static boolean apkTargetsApiLevelLessThanOrEqualToOMR1(PackageParser.Package pkg) {
+ int targetSdkVersion = pkg.applicationInfo.targetSdkVersion;
+ return targetSdkVersion < Build.VERSION_CODES.P;
+ }
+
+ /**
+ * Add an implicit dependency.
+ *
+ * <p>If the package has an existing dependency on {@code existingLibrary} then prefix it with
+ * the {@code implicitDependency} if it is not already in the list of libraries.
+ *
+ * @param pkg the {@link PackageParser.Package} to update.
+ * @param existingLibrary the existing library.
+ * @param implicitDependency the implicit dependency to add
+ */
+ void prefixImplicitDependency(PackageParser.Package pkg, String existingLibrary,
+ String implicitDependency) {
+ ArrayList<String> usesLibraries = pkg.usesLibraries;
+ ArrayList<String> usesOptionalLibraries = pkg.usesOptionalLibraries;
+
+ if (!isLibraryPresent(usesLibraries, usesOptionalLibraries, implicitDependency)) {
+ if (ArrayUtils.contains(usesLibraries, existingLibrary)) {
+ prefix(usesLibraries, implicitDependency);
+ } else if (ArrayUtils.contains(usesOptionalLibraries, existingLibrary)) {
+ prefix(usesOptionalLibraries, implicitDependency);
+ }
+
+ pkg.usesLibraries = usesLibraries;
+ pkg.usesOptionalLibraries = usesOptionalLibraries;
+ }
+ }
+
+ void prefixRequiredLibrary(PackageParser.Package pkg, String libraryName) {
+ ArrayList<String> usesLibraries = pkg.usesLibraries;
+ ArrayList<String> usesOptionalLibraries = pkg.usesOptionalLibraries;
+
+ boolean alreadyPresent = isLibraryPresent(
+ usesLibraries, usesOptionalLibraries, libraryName);
+ if (!alreadyPresent) {
+ usesLibraries = prefix(usesLibraries, libraryName);
+
+ pkg.usesLibraries = usesLibraries;
+ }
+ }
+}
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 069b2d4e02b5..f471a1d9b36c 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -27,11 +27,14 @@ import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+import android.os.BaseBundle;
+import android.os.PersistableBundle;
import android.util.ArraySet;
import com.android.internal.util.ArrayUtils;
import java.util.Arrays;
+import java.util.Objects;
/**
* Per-user state information about a package.
@@ -44,6 +47,10 @@ public class PackageUserState {
public boolean notLaunched;
public boolean hidden; // Is the app restricted by owner / admin
public boolean suspended;
+ public String suspendingPackage;
+ public String dialogMessage; // Message to show when a suspended package launch attempt is made
+ public PersistableBundle suspendedAppExtras;
+ public PersistableBundle suspendedLauncherExtras;
public boolean instantApp;
public boolean virtualPreload;
public int enabled;
@@ -52,6 +59,7 @@ public class PackageUserState {
public int appLinkGeneration;
public int categoryHint = ApplicationInfo.CATEGORY_UNDEFINED;
public int installReason;
+ public String harmfulAppWarning;
public ArraySet<String> disabledComponents;
public ArraySet<String> enabledComponents;
@@ -75,6 +83,10 @@ public class PackageUserState {
notLaunched = o.notLaunched;
hidden = o.hidden;
suspended = o.suspended;
+ suspendingPackage = o.suspendingPackage;
+ dialogMessage = o.dialogMessage;
+ suspendedAppExtras = o.suspendedAppExtras;
+ suspendedLauncherExtras = o.suspendedLauncherExtras;
instantApp = o.instantApp;
virtualPreload = o.virtualPreload;
enabled = o.enabled;
@@ -87,6 +99,7 @@ public class PackageUserState {
enabledComponents = ArrayUtils.cloneOrNull(o.enabledComponents);
overlayPaths =
o.overlayPaths == null ? null : Arrays.copyOf(o.overlayPaths, o.overlayPaths.length);
+ harmfulAppWarning = o.harmfulAppWarning;
}
/**
@@ -193,6 +206,23 @@ public class PackageUserState {
if (suspended != oldState.suspended) {
return false;
}
+ if (suspended) {
+ if (suspendingPackage == null
+ || !suspendingPackage.equals(oldState.suspendingPackage)) {
+ return false;
+ }
+ if (!Objects.equals(dialogMessage, oldState.dialogMessage)) {
+ return false;
+ }
+ if (!BaseBundle.kindofEquals(suspendedAppExtras,
+ oldState.suspendedAppExtras)) {
+ return false;
+ }
+ if (!BaseBundle.kindofEquals(suspendedLauncherExtras,
+ oldState.suspendedLauncherExtras)) {
+ return false;
+ }
+ }
if (instantApp != oldState.instantApp) {
return false;
}
@@ -247,6 +277,11 @@ public class PackageUserState {
}
}
}
+ if (harmfulAppWarning == null && oldState.harmfulAppWarning != null
+ || (harmfulAppWarning != null
+ && !harmfulAppWarning.equals(oldState.harmfulAppWarning))) {
+ return false;
+ }
return true;
}
}
diff --git a/core/java/android/content/pm/PermissionGroupInfo.java b/core/java/android/content/pm/PermissionGroupInfo.java
index 452bf0d2b6a1..7c4478d0b689 100644
--- a/core/java/android/content/pm/PermissionGroupInfo.java
+++ b/core/java/android/content/pm/PermissionGroupInfo.java
@@ -16,6 +16,8 @@
package android.content.pm;
+import android.annotation.StringRes;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -34,6 +36,15 @@ public class PermissionGroupInfo extends PackageItemInfo implements Parcelable {
public int descriptionRes;
/**
+ * A string resource identifier (in the package's resources) used to request the permissions.
+ * From the "request" attribute or, if not set, 0.
+ *
+ * @hide
+ */
+ @SystemApi
+ public @StringRes int requestRes;
+
+ /**
* The description string provided in the AndroidManifest file, if any. You
* probably don't want to use this, since it will be null if the description
* is in a resource. You probably want
@@ -64,6 +75,7 @@ public class PermissionGroupInfo extends PackageItemInfo implements Parcelable {
public PermissionGroupInfo(PermissionGroupInfo orig) {
super(orig);
descriptionRes = orig.descriptionRes;
+ requestRes = orig.requestRes;
nonLocalizedDescription = orig.nonLocalizedDescription;
flags = orig.flags;
priority = orig.priority;
@@ -106,6 +118,7 @@ public class PermissionGroupInfo extends PackageItemInfo implements Parcelable {
public void writeToParcel(Parcel dest, int parcelableFlags) {
super.writeToParcel(dest, parcelableFlags);
dest.writeInt(descriptionRes);
+ dest.writeInt(requestRes);
TextUtils.writeToParcel(nonLocalizedDescription, dest, parcelableFlags);
dest.writeInt(flags);
dest.writeInt(priority);
@@ -124,6 +137,7 @@ public class PermissionGroupInfo extends PackageItemInfo implements Parcelable {
private PermissionGroupInfo(Parcel source) {
super(source);
descriptionRes = source.readInt();
+ requestRes = source.readInt();
nonLocalizedDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
flags = source.readInt();
priority = source.readInt();
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 797db5497390..938409af90e9 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -16,12 +16,16 @@
package android.content.pm;
+import android.annotation.IntDef;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Information you can retrieve about a particular security permission
* known to the system. This corresponds to information collected from the
@@ -56,6 +60,16 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
@Deprecated
public static final int PROTECTION_SIGNATURE_OR_SYSTEM = 3;
+ /** @hide */
+ @IntDef(flag = false, prefix = { "PROTECTION_" }, value = {
+ PROTECTION_NORMAL,
+ PROTECTION_DANGEROUS,
+ PROTECTION_SIGNATURE,
+ PROTECTION_SIGNATURE_OR_SYSTEM,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Protection {}
+
/**
* Additional flag for {@link #protectionLevel}, corresponding
* to the <code>privileged</code> value of
@@ -135,24 +149,91 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
public static final int PROTECTION_FLAG_RUNTIME_ONLY = 0x2000;
/**
+ * Additional flag for {@link #protectionLevel}, corresponding
+ * to the <code>oem</code> value of
+ * {@link android.R.attr#protectionLevel}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int PROTECTION_FLAG_OEM = 0x4000;
+
+ /**
+ * Additional flag for {${link #protectionLevel}, corresponding
+ * to the <code>vendorPrivileged</code> value of
+ * {@link android.R.attr#protectionLevel}.
+ *
+ * @hide
+ */
+ @TestApi
+ public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 0x8000;
+
+ /**
+ * Additional flag for {@link #protectionLevel}, corresponding
+ * to the <code>text_classifier</code> value of
+ * {@link android.R.attr#protectionLevel}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 0x10000;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "PROTECTION_FLAG_" }, value = {
+ PROTECTION_FLAG_PRIVILEGED,
+ PROTECTION_FLAG_SYSTEM,
+ PROTECTION_FLAG_DEVELOPMENT,
+ PROTECTION_FLAG_APPOP,
+ PROTECTION_FLAG_PRE23,
+ PROTECTION_FLAG_INSTALLER,
+ PROTECTION_FLAG_VERIFIER,
+ PROTECTION_FLAG_PREINSTALLED,
+ PROTECTION_FLAG_SETUP,
+ PROTECTION_FLAG_INSTANT,
+ PROTECTION_FLAG_RUNTIME_ONLY,
+ PROTECTION_FLAG_OEM,
+ PROTECTION_FLAG_VENDOR_PRIVILEGED,
+ PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ProtectionFlags {}
+
+ /**
* Mask for {@link #protectionLevel}: the basic protection type.
+ *
+ * @deprecated Use #getProtection() instead.
*/
+ @Deprecated
public static final int PROTECTION_MASK_BASE = 0xf;
/**
* Mask for {@link #protectionLevel}: additional flag bits.
+ *
+ * @deprecated Use #getProtectionFlags() instead.
*/
+ @Deprecated
public static final int PROTECTION_MASK_FLAGS = 0xfff0;
/**
* The level of access this permission is protecting, as per
- * {@link android.R.attr#protectionLevel}. Values may be
- * {@link #PROTECTION_NORMAL}, {@link #PROTECTION_DANGEROUS}, or
- * {@link #PROTECTION_SIGNATURE}. May also include the additional
- * flags {@link #PROTECTION_FLAG_SYSTEM} or {@link #PROTECTION_FLAG_DEVELOPMENT}
- * (which only make sense in combination with the base
- * {@link #PROTECTION_SIGNATURE}.
+ * {@link android.R.attr#protectionLevel}. Consists of
+ * a base permission type and zero or more flags. Use the following functions
+ * to extract them.
+ *
+ * <pre>
+ * int basePermissionType = permissionInfo.getProtection();
+ * int permissionFlags = permissionInfo.getProtectionFlags();
+ * </pre>
+ *
+ * <p></p>Base permission types are {@link #PROTECTION_NORMAL},
+ * {@link #PROTECTION_DANGEROUS}, {@link #PROTECTION_SIGNATURE}
+ * and the deprecated {@link #PROTECTION_SIGNATURE_OR_SYSTEM}.
+ * Flags are listed under {@link android.R.attr#protectionLevel}.
+ *
+ * @deprecated Use #getProtection() and #getProtectionFlags() instead.
*/
+ @Deprecated
public int protectionLevel;
/**
@@ -195,6 +276,15 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
public int descriptionRes;
/**
+ * A string resource identifier (in the package's resources) used to request the permissions.
+ * From the "request" attribute or, if not set, 0.
+ *
+ * @hide
+ */
+ @SystemApi
+ public int requestRes;
+
+ /**
* The description string provided in the AndroidManifest file, if any. You
* probably don't want to use this, since it will be null if the description
* is in a resource. You probably want
@@ -207,13 +297,19 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
if (level == PROTECTION_SIGNATURE_OR_SYSTEM) {
level = PROTECTION_SIGNATURE | PROTECTION_FLAG_PRIVILEGED;
}
+ if ((level & PROTECTION_FLAG_VENDOR_PRIVILEGED) != 0
+ && (level & PROTECTION_FLAG_PRIVILEGED) == 0) {
+ // 'vendorPrivileged' must be 'privileged'. If not,
+ // drop the vendorPrivileged.
+ level = level & ~PROTECTION_FLAG_VENDOR_PRIVILEGED;
+ }
return level;
}
/** @hide */
public static String protectionToString(int level) {
String protLevel = "????";
- switch (level&PROTECTION_MASK_BASE) {
+ switch (level & PROTECTION_MASK_BASE) {
case PermissionInfo.PROTECTION_DANGEROUS:
protLevel = "dangerous";
break;
@@ -227,36 +323,45 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
protLevel = "signatureOrSystem";
break;
}
- if ((level&PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0) {
+ if ((level & PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0) {
protLevel += "|privileged";
}
- if ((level&PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) {
+ if ((level & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) {
protLevel += "|development";
}
- if ((level&PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
+ if ((level & PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
protLevel += "|appop";
}
- if ((level&PermissionInfo.PROTECTION_FLAG_PRE23) != 0) {
+ if ((level & PermissionInfo.PROTECTION_FLAG_PRE23) != 0) {
protLevel += "|pre23";
}
- if ((level&PermissionInfo.PROTECTION_FLAG_INSTALLER) != 0) {
+ if ((level & PermissionInfo.PROTECTION_FLAG_INSTALLER) != 0) {
protLevel += "|installer";
}
- if ((level&PermissionInfo.PROTECTION_FLAG_VERIFIER) != 0) {
+ if ((level & PermissionInfo.PROTECTION_FLAG_VERIFIER) != 0) {
protLevel += "|verifier";
}
- if ((level&PermissionInfo.PROTECTION_FLAG_PREINSTALLED) != 0) {
+ if ((level & PermissionInfo.PROTECTION_FLAG_PREINSTALLED) != 0) {
protLevel += "|preinstalled";
}
- if ((level&PermissionInfo.PROTECTION_FLAG_SETUP) != 0) {
+ if ((level & PermissionInfo.PROTECTION_FLAG_SETUP) != 0) {
protLevel += "|setup";
}
- if ((level&PermissionInfo.PROTECTION_FLAG_INSTANT) != 0) {
+ if ((level & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0) {
protLevel += "|instant";
}
- if ((level&PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0) {
+ if ((level & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0) {
protLevel += "|runtime";
}
+ if ((level & PermissionInfo.PROTECTION_FLAG_OEM) != 0) {
+ protLevel += "|oem";
+ }
+ if ((level & PermissionInfo.PROTECTION_FLAG_VENDOR_PRIVILEGED) != 0) {
+ protLevel += "|vendorPrivileged";
+ }
+ if ((level & PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER) != 0) {
+ protLevel += "|textClassifier";
+ }
return protLevel;
}
@@ -269,6 +374,7 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
flags = orig.flags;
group = orig.group;
descriptionRes = orig.descriptionRes;
+ requestRes = orig.requestRes;
nonLocalizedDescription = orig.nonLocalizedDescription;
}
@@ -296,30 +402,69 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
return null;
}
+ /**
+ * Return the base permission type.
+ */
+ @Protection
+ public int getProtection() {
+ return protectionLevel & PROTECTION_MASK_BASE;
+ }
+
+ /**
+ * Return the additional flags in {@link #protectionLevel}.
+ */
+ @ProtectionFlags
+ public int getProtectionFlags() {
+ return protectionLevel & ~PROTECTION_MASK_BASE;
+ }
+
+ @Override
public String toString() {
return "PermissionInfo{"
+ Integer.toHexString(System.identityHashCode(this))
+ " " + name + "}";
}
+ @Override
public int describeContents() {
return 0;
}
+ @Override
public void writeToParcel(Parcel dest, int parcelableFlags) {
super.writeToParcel(dest, parcelableFlags);
dest.writeInt(protectionLevel);
dest.writeInt(flags);
dest.writeString(group);
dest.writeInt(descriptionRes);
+ dest.writeInt(requestRes);
TextUtils.writeToParcel(nonLocalizedDescription, dest, parcelableFlags);
}
+ /** @hide */
+ public int calculateFootprint() {
+ int size = name.length();
+ if (nonLocalizedLabel != null) {
+ size += nonLocalizedLabel.length();
+ }
+ if (nonLocalizedDescription != null) {
+ size += nonLocalizedDescription.length();
+ }
+ return size;
+ }
+
+ /** @hide */
+ public boolean isAppOp() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0;
+ }
+
public static final Creator<PermissionInfo> CREATOR =
new Creator<PermissionInfo>() {
+ @Override
public PermissionInfo createFromParcel(Parcel source) {
return new PermissionInfo(source);
}
+ @Override
public PermissionInfo[] newArray(int size) {
return new PermissionInfo[size];
}
@@ -331,6 +476,7 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
flags = source.readInt();
group = source.readString();
descriptionRes = source.readInt();
+ requestRes = source.readInt();
nonLocalizedDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
}
}
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index aea843adbd48..020e8c22b128 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -16,6 +16,7 @@
package android.content.pm;
+import android.Manifest;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -40,9 +41,12 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
+
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
+import libcore.io.IoUtils;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -61,8 +65,6 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
-import libcore.io.IoUtils;
-
/**
* Cache of registered services. This cache is lazily built by interrogating
* {@link PackageManager} on a per-user basis. It's updated as packages are
@@ -72,7 +74,7 @@ import libcore.io.IoUtils;
* <p>
* The services are referred to by type V and are made available via the
* {@link #getServiceInfo} method.
- *
+ *
* @hide
*/
public abstract class RegisteredServicesCache<V> {
@@ -98,6 +100,8 @@ public abstract class RegisteredServicesCache<V> {
Map<V, ServiceInfo<V>> services = null;
@GuardedBy("mServicesLock")
boolean mPersistentServicesFileDidNotExist = true;
+ @GuardedBy("mServicesLock")
+ boolean mBindInstantServiceAllowed = false;
}
@GuardedBy("mServicesLock")
@@ -273,7 +277,7 @@ public abstract class RegisteredServicesCache<V> {
Log.d(TAG, "notifyListener: " + type + " is " + (removed ? "removed" : "added"));
}
RegisteredServicesCacheListener<V> listener;
- Handler handler;
+ Handler handler;
synchronized (this) {
listener = mListener;
handler = mHandler;
@@ -281,7 +285,7 @@ public abstract class RegisteredServicesCache<V> {
if (listener == null) {
return;
}
-
+
final RegisteredServicesCacheListener<V> listener2 = listener;
handler.post(new Runnable() {
public void run() {
@@ -361,7 +365,7 @@ public abstract class RegisteredServicesCache<V> {
}
IntArray updatedUids = null;
for (ServiceInfo<V> service : allServices) {
- int versionCode = service.componentInfo.applicationInfo.versionCode;
+ long versionCode = service.componentInfo.applicationInfo.versionCode;
String pkg = service.componentInfo.packageName;
ApplicationInfo newAppInfo = null;
try {
@@ -387,6 +391,34 @@ public abstract class RegisteredServicesCache<V> {
}
}
+ /**
+ * @return whether the binding to service is allowed for instant apps.
+ */
+ public boolean getBindInstantServiceAllowed(int userId) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.MANAGE_BIND_INSTANT_SERVICE,
+ "getBindInstantServiceAllowed");
+
+ synchronized (mServicesLock) {
+ final UserServices<V> user = findOrCreateUserLocked(userId);
+ return user.mBindInstantServiceAllowed;
+ }
+ }
+
+ /**
+ * Set whether the binding to service is allowed or not for instant apps.
+ */
+ public void setBindInstantServiceAllowed(int userId, boolean allowed) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.MANAGE_BIND_INSTANT_SERVICE,
+ "setBindInstantServiceAllowed");
+
+ synchronized (mServicesLock) {
+ final UserServices<V> user = findOrCreateUserLocked(userId);
+ user.mBindInstantServiceAllowed = allowed;
+ }
+ }
+
@VisibleForTesting
protected boolean inSystemImage(int callerUid) {
String[] packages = mContext.getPackageManager().getPackagesForUid(callerUid);
@@ -409,10 +441,16 @@ public abstract class RegisteredServicesCache<V> {
@VisibleForTesting
protected List<ResolveInfo> queryIntentServices(int userId) {
final PackageManager pm = mContext.getPackageManager();
- return pm.queryIntentServicesAsUser(new Intent(mInterfaceName),
- PackageManager.GET_META_DATA | PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- userId);
+ int flags = PackageManager.GET_META_DATA
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+ synchronized (mServicesLock) {
+ final UserServices<V> user = findOrCreateUserLocked(userId);
+ if (user.mBindInstantServiceAllowed) {
+ flags |= PackageManager.MATCH_INSTANT;
+ }
+ }
+ return pm.queryIntentServicesAsUser(new Intent(mInterfaceName), flags, userId);
}
/**
diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java
index 799316700b4d..fc2eba282352 100644
--- a/core/java/android/content/pm/ResolveInfo.java
+++ b/core/java/android/content/pm/ResolveInfo.java
@@ -222,6 +222,40 @@ public class ResolveInfo implements Parcelable {
}
/**
+ * @return The resource that would be used when loading
+ * the label for this resolve info.
+ *
+ * @hide
+ */
+ public int resolveLabelResId() {
+ if (labelRes != 0) {
+ return labelRes;
+ }
+ final ComponentInfo componentInfo = getComponentInfo();
+ if (componentInfo.labelRes != 0) {
+ return componentInfo.labelRes;
+ }
+ return componentInfo.applicationInfo.labelRes;
+ }
+
+ /**
+ * @return The resource that would be used when loading
+ * the icon for this resolve info.
+ *
+ * @hide
+ */
+ public int resolveIconResId() {
+ if (icon != 0) {
+ return icon;
+ }
+ final ComponentInfo componentInfo = getComponentInfo();
+ if (componentInfo.icon != 0) {
+ return componentInfo.icon;
+ }
+ return componentInfo.applicationInfo.icon;
+ }
+
+ /**
* Retrieve the current graphical icon associated with this resolution. This
* will call back on the given PackageManager to load the icon from
* the application.
@@ -243,7 +277,7 @@ public class ResolveInfo implements Parcelable {
dr = pm.getDrawable(ci.packageName, iconResourceId, ai);
}
if (dr != null) {
- return pm.getUserBadgedIcon(dr, new UserHandle(UserHandle.myUserId()));
+ return pm.getUserBadgedIcon(dr, new UserHandle(pm.getUserId()));
}
return ci.loadIcon(pm);
}
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index 7d301a3154f0..33bc9515409f 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -36,13 +36,11 @@ import java.util.List;
public final class SharedLibraryInfo implements Parcelable {
/** @hide */
- @IntDef(
- flag = true,
- value = {
- TYPE_BUILTIN,
- TYPE_DYNAMIC,
- TYPE_STATIC,
- })
+ @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+ TYPE_BUILTIN,
+ TYPE_DYNAMIC,
+ TYPE_STATIC,
+ })
@Retention(RetentionPolicy.SOURCE)
@interface Type{}
@@ -73,8 +71,7 @@ public final class SharedLibraryInfo implements Parcelable {
private final String mName;
- // TODO: Make long when we change the paltform to use longs
- private final int mVersion;
+ private final long mVersion;
private final @Type int mType;
private final VersionedPackage mDeclaringPackage;
private final List<VersionedPackage> mDependentPackages;
@@ -90,7 +87,7 @@ public final class SharedLibraryInfo implements Parcelable {
*
* @hide
*/
- public SharedLibraryInfo(String name, int version, int type,
+ public SharedLibraryInfo(String name, long version, int type,
VersionedPackage declaringPackage, List<VersionedPackage> dependentPackages) {
mName = name;
mVersion = version;
@@ -100,7 +97,7 @@ public final class SharedLibraryInfo implements Parcelable {
}
private SharedLibraryInfo(Parcel parcel) {
- this(parcel.readString(), parcel.readInt(), parcel.readInt(),
+ this(parcel.readString(), parcel.readLong(), parcel.readInt(),
parcel.readParcelable(null), parcel.readArrayList(null));
}
@@ -124,6 +121,14 @@ public final class SharedLibraryInfo implements Parcelable {
}
/**
+ * @deprecated Use {@link #getLongVersion()} instead.
+ */
+ @Deprecated
+ public @IntRange(from = -1) int getVersion() {
+ return mVersion < 0 ? (int) mVersion : (int) (mVersion & 0x7fffffff);
+ }
+
+ /**
* Gets the version of the library. For {@link #TYPE_STATIC static} libraries
* this is the declared version and for {@link #TYPE_DYNAMIC dynamic} and
* {@link #TYPE_BUILTIN builtin} it is {@link #VERSION_UNDEFINED} as these
@@ -131,7 +136,7 @@ public final class SharedLibraryInfo implements Parcelable {
*
* @return The version.
*/
- public @IntRange(from = -1) int getVersion() {
+ public @IntRange(from = -1) long getLongVersion() {
return mVersion;
}
@@ -192,7 +197,7 @@ public final class SharedLibraryInfo implements Parcelable {
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeString(mName);
- parcel.writeInt(mVersion);
+ parcel.writeLong(mVersion);
parcel.writeInt(mType);
parcel.writeParcelable(mDeclaringPackage, flags);
parcel.writeList(mDependentPackages);
diff --git a/core/java/android/content/pm/SharedLibraryNames.java b/core/java/android/content/pm/SharedLibraryNames.java
new file mode 100644
index 000000000000..83e86636424a
--- /dev/null
+++ b/core/java/android/content/pm/SharedLibraryNames.java
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+package android.content.pm;
+
+/**
+ * A set of shared library names
+ *
+ * @hide
+ */
+public class SharedLibraryNames {
+
+ static final String ANDROID_TEST_BASE = "android.test.base";
+
+ static final String ANDROID_TEST_MOCK = "android.test.mock";
+
+ static final String ANDROID_TEST_RUNNER = "android.test.runner";
+
+ static final String ORG_APACHE_HTTP_LEGACY = "org.apache.http.legacy";
+}
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index d3a3560c7229..ea476b0abf33 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -18,6 +18,7 @@ package android.content.pm;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.annotation.UserIdInt;
import android.app.TaskStackBuilder;
import android.content.ComponentName;
@@ -100,9 +101,15 @@ public final class ShortcutInfo implements Parcelable {
/** @hide When this is set, the bitmap icon is waiting to be saved. */
public static final int FLAG_ICON_FILE_PENDING_SAVE = 1 << 11;
+ /**
+ * "Shadow" shortcuts are the ones that are restored, but the owner package hasn't been
+ * installed yet.
+ * @hide
+ */
+ public static final int FLAG_SHADOW = 1 << 12;
+
/** @hide */
- @IntDef(flag = true,
- value = {
+ @IntDef(flag = true, prefix = { "FLAG_" }, value = {
FLAG_DYNAMIC,
FLAG_PINNED,
FLAG_HAS_ICON_RES,
@@ -145,19 +152,145 @@ public final class ShortcutInfo implements Parcelable {
| CLONE_REMOVE_RES_NAMES;
/** @hide */
- @IntDef(flag = true,
- value = {
- CLONE_REMOVE_ICON,
- CLONE_REMOVE_INTENT,
- CLONE_REMOVE_NON_KEY_INFO,
- CLONE_REMOVE_RES_NAMES,
- CLONE_REMOVE_FOR_CREATOR,
- CLONE_REMOVE_FOR_LAUNCHER
- })
+ @IntDef(flag = true, prefix = { "CLONE_" }, value = {
+ CLONE_REMOVE_ICON,
+ CLONE_REMOVE_INTENT,
+ CLONE_REMOVE_NON_KEY_INFO,
+ CLONE_REMOVE_RES_NAMES,
+ CLONE_REMOVE_FOR_CREATOR,
+ CLONE_REMOVE_FOR_LAUNCHER
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface CloneFlags {}
/**
+ * Shortcut is not disabled.
+ */
+ public static final int DISABLED_REASON_NOT_DISABLED = 0;
+
+ /**
+ * Shortcut has been disabled by the publisher app with the
+ * {@link ShortcutManager#disableShortcuts(List)} API.
+ */
+ public static final int DISABLED_REASON_BY_APP = 1;
+
+ /**
+ * Shortcut has been disabled due to changes to the publisher app. (e.g. a manifest shortcut
+ * no longer exists.)
+ */
+ public static final int DISABLED_REASON_APP_CHANGED = 2;
+
+ /**
+ * Shortcut is disabled for an unknown reason.
+ */
+ public static final int DISABLED_REASON_UNKNOWN = 3;
+
+ /**
+ * A disabled reason that's equal to or bigger than this is due to backup and restore issue.
+ * A shortcut with such a reason wil be visible to the launcher, but not to the publisher.
+ * ({@link #isVisibleToPublisher()} will be false.)
+ */
+ private static final int DISABLED_REASON_RESTORE_ISSUE_START = 100;
+
+ /**
+ * Shortcut has been restored from the previous device, but the publisher app on the current
+ * device is of a lower version. The shortcut will not be usable until the app is upgraded to
+ * the same version or higher.
+ */
+ public static final int DISABLED_REASON_VERSION_LOWER = 100;
+
+ /**
+ * Shortcut has not been restored because the publisher app does not support backup and restore.
+ */
+ public static final int DISABLED_REASON_BACKUP_NOT_SUPPORTED = 101;
+
+ /**
+ * Shortcut has not been restored because the publisher app's signature has changed.
+ */
+ public static final int DISABLED_REASON_SIGNATURE_MISMATCH = 102;
+
+ /**
+ * Shortcut has not been restored for unknown reason.
+ */
+ public static final int DISABLED_REASON_OTHER_RESTORE_ISSUE = 103;
+
+ /** @hide */
+ @IntDef(prefix = { "DISABLED_REASON_" }, value = {
+ DISABLED_REASON_NOT_DISABLED,
+ DISABLED_REASON_BY_APP,
+ DISABLED_REASON_APP_CHANGED,
+ DISABLED_REASON_UNKNOWN,
+ DISABLED_REASON_VERSION_LOWER,
+ DISABLED_REASON_BACKUP_NOT_SUPPORTED,
+ DISABLED_REASON_SIGNATURE_MISMATCH,
+ DISABLED_REASON_OTHER_RESTORE_ISSUE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DisabledReason{}
+
+ /**
+ * Return a label for disabled reasons, which are *not* supposed to be shown to the user.
+ * @hide
+ */
+ public static String getDisabledReasonDebugString(@DisabledReason int disabledReason) {
+ switch (disabledReason) {
+ case DISABLED_REASON_NOT_DISABLED:
+ return "[Not disabled]";
+ case DISABLED_REASON_BY_APP:
+ return "[Disabled: by app]";
+ case DISABLED_REASON_APP_CHANGED:
+ return "[Disabled: app changed]";
+ case DISABLED_REASON_VERSION_LOWER:
+ return "[Disabled: lower version]";
+ case DISABLED_REASON_BACKUP_NOT_SUPPORTED:
+ return "[Disabled: backup not supported]";
+ case DISABLED_REASON_SIGNATURE_MISMATCH:
+ return "[Disabled: signature mismatch]";
+ case DISABLED_REASON_OTHER_RESTORE_ISSUE:
+ return "[Disabled: unknown restore issue]";
+ }
+ return "[Disabled: unknown reason:" + disabledReason + "]";
+ }
+
+ /**
+ * Return a label for a disabled reason for shortcuts that are disabled due to a backup and
+ * restore issue. If the reason is not due to backup & restore, then it'll return null.
+ *
+ * This method returns localized, user-facing strings, which will be returned by
+ * {@link #getDisabledMessage()}.
+ *
+ * @hide
+ */
+ public static String getDisabledReasonForRestoreIssue(Context context,
+ @DisabledReason int disabledReason) {
+ final Resources res = context.getResources();
+
+ switch (disabledReason) {
+ case DISABLED_REASON_VERSION_LOWER:
+ return res.getString(
+ com.android.internal.R.string.shortcut_restored_on_lower_version);
+ case DISABLED_REASON_BACKUP_NOT_SUPPORTED:
+ return res.getString(
+ com.android.internal.R.string.shortcut_restore_not_supported);
+ case DISABLED_REASON_SIGNATURE_MISMATCH:
+ return res.getString(
+ com.android.internal.R.string.shortcut_restore_signature_mismatch);
+ case DISABLED_REASON_OTHER_RESTORE_ISSUE:
+ return res.getString(
+ com.android.internal.R.string.shortcut_restore_unknown_issue);
+ case DISABLED_REASON_UNKNOWN:
+ return res.getString(
+ com.android.internal.R.string.shortcut_disabled_reason_unknown);
+ }
+ return null;
+ }
+
+ /** @hide */
+ public static boolean isDisabledForRestoreIssue(@DisabledReason int disabledReason) {
+ return disabledReason >= DISABLED_REASON_RESTORE_ISSUE_START;
+ }
+
+ /**
* Shortcut category for messaging related actions, such as chat.
*/
public static final String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
@@ -240,6 +373,11 @@ public final class ShortcutInfo implements Parcelable {
private final int mUserId;
+ /** @hide */
+ public static final int VERSION_CODE_UNKNOWN = -1;
+
+ private int mDisabledReason;
+
private ShortcutInfo(Builder b) {
mUserId = b.mContext.getUserId();
@@ -352,6 +490,7 @@ public final class ShortcutInfo implements Parcelable {
mActivity = source.mActivity;
mFlags = source.mFlags;
mLastChangedTimestamp = source.mLastChangedTimestamp;
+ mDisabledReason = source.mDisabledReason;
// Just always keep it since it's cheep.
mIconResId = source.mIconResId;
@@ -615,13 +754,23 @@ public final class ShortcutInfo implements Parcelable {
/**
* @hide
+ *
+ * @isUpdating set true if it's "update", as opposed to "replace".
*/
- public void ensureUpdatableWith(ShortcutInfo source) {
+ public void ensureUpdatableWith(ShortcutInfo source, boolean isUpdating) {
+ if (isUpdating) {
+ Preconditions.checkState(isVisibleToPublisher(),
+ "[Framework BUG] Invisible shortcuts can't be updated");
+ }
Preconditions.checkState(mUserId == source.mUserId, "Owner User ID must match");
Preconditions.checkState(mId.equals(source.mId), "ID must match");
Preconditions.checkState(mPackageName.equals(source.mPackageName),
"Package name must match");
- Preconditions.checkState(!isImmutable(), "Target ShortcutInfo is immutable");
+
+ if (isVisibleToPublisher()) {
+ // Don't do this check for restore-blocked shortcuts.
+ Preconditions.checkState(!isImmutable(), "Target ShortcutInfo is immutable");
+ }
}
/**
@@ -638,7 +787,7 @@ public final class ShortcutInfo implements Parcelable {
* @hide
*/
public void copyNonNullFieldsFrom(ShortcutInfo source) {
- ensureUpdatableWith(source);
+ ensureUpdatableWith(source, /*isUpdating=*/ true);
if (source.mActivity != null) {
mActivity = source.mActivity;
@@ -1169,6 +1318,19 @@ public final class ShortcutInfo implements Parcelable {
return mDisabledMessageResId;
}
+ /** @hide */
+ public void setDisabledReason(@DisabledReason int reason) {
+ mDisabledReason = reason;
+ }
+
+ /**
+ * Returns why a shortcut has been disabled.
+ */
+ @DisabledReason
+ public int getDisabledReason() {
+ return mDisabledReason;
+ }
+
/**
* Return the shortcut's categories.
*
@@ -1403,6 +1565,21 @@ public final class ShortcutInfo implements Parcelable {
return hasFlags(FLAG_IMMUTABLE);
}
+ /** @hide */
+ public boolean isDynamicVisible() {
+ return isDynamic() && isVisibleToPublisher();
+ }
+
+ /** @hide */
+ public boolean isPinnedVisible() {
+ return isPinned() && isVisibleToPublisher();
+ }
+
+ /** @hide */
+ public boolean isManifestVisible() {
+ return isDeclaredInManifest() && isVisibleToPublisher();
+ }
+
/**
* Return if a shortcut is immutable, in which case it cannot be modified with any of
* {@link ShortcutManager} APIs.
@@ -1491,6 +1668,18 @@ public final class ShortcutInfo implements Parcelable {
}
/**
+ * When the system wasn't able to restore a shortcut, it'll still be registered to the system
+ * but disabled, and such shortcuts will not be visible to the publisher. They're still visible
+ * to launchers though.
+ *
+ * @hide
+ */
+ @TestApi
+ public boolean isVisibleToPublisher() {
+ return !isDisabledForRestoreIssue(mDisabledReason);
+ }
+
+ /**
* Return whether a shortcut only contains "key" information only or not. If true, only the
* following fields are available.
* <ul>
@@ -1668,6 +1857,7 @@ public final class ShortcutInfo implements Parcelable {
mFlags = source.readInt();
mIconResId = source.readInt();
mLastChangedTimestamp = source.readLong();
+ mDisabledReason = source.readInt();
if (source.readInt() == 0) {
return; // key information only.
@@ -1711,6 +1901,7 @@ public final class ShortcutInfo implements Parcelable {
dest.writeInt(mFlags);
dest.writeInt(mIconResId);
dest.writeLong(mLastChangedTimestamp);
+ dest.writeInt(mDisabledReason);
if (hasKeyFieldsOnly()) {
dest.writeInt(0);
@@ -1763,21 +1954,43 @@ public final class ShortcutInfo implements Parcelable {
return 0;
}
+
/**
* Return a string representation, intended for logging. Some fields will be retracted.
*/
@Override
public String toString() {
- return toStringInner(/* secure =*/ true, /* includeInternalData =*/ false);
+ return toStringInner(/* secure =*/ true, /* includeInternalData =*/ false,
+ /*indent=*/ null);
}
/** @hide */
public String toInsecureString() {
- return toStringInner(/* secure =*/ false, /* includeInternalData =*/ true);
+ return toStringInner(/* secure =*/ false, /* includeInternalData =*/ true,
+ /*indent=*/ null);
}
- private String toStringInner(boolean secure, boolean includeInternalData) {
+ /** @hide */
+ public String toDumpString(String indent) {
+ return toStringInner(/* secure =*/ false, /* includeInternalData =*/ true, indent);
+ }
+
+ private void addIndentOrComma(StringBuilder sb, String indent) {
+ if (indent != null) {
+ sb.append("\n ");
+ sb.append(indent);
+ } else {
+ sb.append(", ");
+ }
+ }
+
+ private String toStringInner(boolean secure, boolean includeInternalData, String indent) {
final StringBuilder sb = new StringBuilder();
+
+ if (indent != null) {
+ sb.append(indent);
+ }
+
sb.append("ShortcutInfo {");
sb.append("id=");
@@ -1786,48 +1999,59 @@ public final class ShortcutInfo implements Parcelable {
sb.append(", flags=0x");
sb.append(Integer.toHexString(mFlags));
sb.append(" [");
+ if ((mFlags & FLAG_SHADOW) != 0) {
+ // Note the shadow flag isn't actually used anywhere and it's just for dumpsys, so
+ // we don't have an isXxx for this.
+ sb.append("Sdw");
+ }
if (!isEnabled()) {
- sb.append("X");
+ sb.append("Dis");
}
if (isImmutable()) {
sb.append("Im");
}
if (isManifestShortcut()) {
- sb.append("M");
+ sb.append("Man");
}
if (isDynamic()) {
- sb.append("D");
+ sb.append("Dyn");
}
if (isPinned()) {
- sb.append("P");
+ sb.append("Pin");
}
if (hasIconFile()) {
- sb.append("If");
+ sb.append("Ic-f");
}
if (isIconPendingSave()) {
- sb.append("^");
+ sb.append("Pens");
}
if (hasIconResource()) {
- sb.append("Ir");
+ sb.append("Ic-r");
}
if (hasKeyFieldsOnly()) {
- sb.append("K");
+ sb.append("Key");
}
if (hasStringResourcesResolved()) {
- sb.append("Sr");
+ sb.append("Str");
}
if (isReturnedByServer()) {
- sb.append("V");
+ sb.append("Rets");
}
sb.append("]");
- sb.append(", packageName=");
+ addIndentOrComma(sb, indent);
+
+ sb.append("packageName=");
sb.append(mPackageName);
- sb.append(", activity=");
+ addIndentOrComma(sb, indent);
+
+ sb.append("activity=");
sb.append(mActivity);
- sb.append(", shortLabel=");
+ addIndentOrComma(sb, indent);
+
+ sb.append("shortLabel=");
sb.append(secure ? "***" : mTitle);
sb.append(", resId=");
sb.append(mTitleResId);
@@ -1835,7 +2059,9 @@ public final class ShortcutInfo implements Parcelable {
sb.append(mTitleResName);
sb.append("]");
- sb.append(", longLabel=");
+ addIndentOrComma(sb, indent);
+
+ sb.append("longLabel=");
sb.append(secure ? "***" : mText);
sb.append(", resId=");
sb.append(mTextResId);
@@ -1843,7 +2069,9 @@ public final class ShortcutInfo implements Parcelable {
sb.append(mTextResName);
sb.append("]");
- sb.append(", disabledMessage=");
+ addIndentOrComma(sb, indent);
+
+ sb.append("disabledMessage=");
sb.append(secure ? "***" : mDisabledMessage);
sb.append(", resId=");
sb.append(mDisabledMessageResId);
@@ -1851,19 +2079,32 @@ public final class ShortcutInfo implements Parcelable {
sb.append(mDisabledMessageResName);
sb.append("]");
- sb.append(", categories=");
+ addIndentOrComma(sb, indent);
+
+ sb.append("disabledReason=");
+ sb.append(getDisabledReasonDebugString(mDisabledReason));
+
+ addIndentOrComma(sb, indent);
+
+ sb.append("categories=");
sb.append(mCategories);
- sb.append(", icon=");
+ addIndentOrComma(sb, indent);
+
+ sb.append("icon=");
sb.append(mIcon);
- sb.append(", rank=");
+ addIndentOrComma(sb, indent);
+
+ sb.append("rank=");
sb.append(mRank);
sb.append(", timestamp=");
sb.append(mLastChangedTimestamp);
- sb.append(", intents=");
+ addIndentOrComma(sb, indent);
+
+ sb.append("intents=");
if (mIntents == null) {
sb.append("null");
} else {
@@ -1885,12 +2126,15 @@ public final class ShortcutInfo implements Parcelable {
}
}
- sb.append(", extras=");
+ addIndentOrComma(sb, indent);
+
+ sb.append("extras=");
sb.append(mExtras);
if (includeInternalData) {
+ addIndentOrComma(sb, indent);
- sb.append(", iconRes=");
+ sb.append("iconRes=");
sb.append(mIconResId);
sb.append("[");
sb.append(mIconResName);
@@ -1912,7 +2156,7 @@ public final class ShortcutInfo implements Parcelable {
CharSequence disabledMessage, int disabledMessageResId, String disabledMessageResName,
Set<String> categories, Intent[] intentsWithExtras, int rank, PersistableBundle extras,
long lastChangedTimestamp,
- int flags, int iconResId, String iconResName, String bitmapPath) {
+ int flags, int iconResId, String iconResName, String bitmapPath, int disabledReason) {
mUserId = userId;
mId = id;
mPackageName = packageName;
@@ -1937,5 +2181,6 @@ public final class ShortcutInfo implements Parcelable {
mIconResId = iconResId;
mIconResName = iconResName;
mBitmapPath = bitmapPath;
+ mDisabledReason = disabledReason;
}
}
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index 61b0eb0b51f8..25e0ccd8d551 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -36,15 +36,26 @@ import com.android.internal.annotations.VisibleForTesting;
import java.util.List;
/**
- * The ShortcutManager manages an app's <em>shortcuts</em>. Shortcuts provide users with quick
- * access to activities other than an app's main activity in the currently-active launcher, provided
- * that the launcher supports app shortcuts. For example, an email app may publish the "compose new
- * email" action, which will directly open the compose activity. The {@link ShortcutInfo} class
- * contains information about each of the shortcuts themselves.
+ * The ShortcutManager performs operations on an app's set of <em>shortcuts</em>. The
+ * {@link ShortcutInfo} class contains information about each of the shortcuts themselves.
+ *
+ * <p>An app's shortcuts represent specific tasks and actions that users can perform within your
+ * app. When a user selects a shortcut in the currently-active launcher, your app opens an activity
+ * other than the app's starting activity, provided that the currently-active launcher supports app
+ * shortcuts.</p>
+ *
+ * <p>The types of shortcuts that you create for your app depend on the app's key use cases. For
+ * example, an email app may publish the "compose new email" shortcut, which allows the app to
+ * directly open the compose activity.</p>
+ *
+ * <p class="note"><b>Note:</b> Only main activities&mdash;activities that handle the
+ * {@link Intent#ACTION_MAIN} action and the {@link Intent#CATEGORY_LAUNCHER} category&mdash;can
+ * have shortcuts. If an app has multiple main activities, you need to define the set of shortcuts
+ * for <em>each</em> activity.
*
* <p>This page discusses the implementation details of the <code>ShortcutManager</code> class. For
- * guidance on performing operations on app shortcuts within your app, see the
- * <a href="/guide/topics/ui/shortcuts.html">App Shortcuts</a> feature guide.
+ * definitions of key terms and guidance on performing operations on shortcuts within your app, see
+ * the <a href="/guide/topics/ui/shortcuts.html">App Shortcuts</a> feature guide.
*
* <h3>Shortcut characteristics</h3>
*
@@ -69,8 +80,8 @@ import java.util.List;
* <ul>
* <li>The user removes it.
* <li>The publisher app associated with the shortcut is uninstalled.
- * <li>The user performs the clear data action on the publisher app from the device's
- * <b>Settings</b> app.
+ * <li>The user selects <b>Clear data</b> from the publisher app's <i>Storage</i> screen, within
+ * the system's <b>Settings</b> app.
* </ul>
*
* <p>Because the system performs
@@ -83,12 +94,17 @@ import java.util.List;
*
* <p>When the launcher displays an app's shortcuts, they should appear in the following order:
*
- * <ul>
- * <li>Static shortcuts (if {@link ShortcutInfo#isDeclaredInManifest()} is {@code true}),
- * and then show dynamic shortcuts (if {@link ShortcutInfo#isDynamic()} is {@code true}).
- * <li>Within each shortcut type (static and dynamic), sort the shortcuts in order of increasing
- * rank according to {@link ShortcutInfo#getRank()}.
- * </ul>
+ * <ol>
+ * <li><b>Static shortcuts:</b> Shortcuts whose {@link ShortcutInfo#isDeclaredInManifest()} method
+ * returns {@code true}.</li>
+ * <li><b>Dynamic shortcuts:</b> Shortcuts whose {@link ShortcutInfo#isDynamic()} method returns
+ * {@code true}.</li>
+ * </ol>
+ *
+ * <p>Within each shortcut type (static and dynamic), shortcuts are sorted in order of increasing
+ * rank according to {@link ShortcutInfo#getRank()}.</p>
+ *
+ * <h4>Shortcut ranks</h4>
*
* <p>Shortcut ranks are non-negative, sequential integers that determine the order in which
* shortcuts appear, assuming that the shortcuts are all in the same category. You can update ranks
@@ -103,64 +119,99 @@ import java.util.List;
*
* <h3>Options for static shortcuts</h3>
*
- * The following list includes descriptions for the different attributes within a static shortcut:
+ * The following list includes descriptions for the different attributes within a static shortcut.
+ * You must provide a value for {@code android:shortcutId} and {@code android:shortcutShortLabel};
+ * all other values are optional.
+ *
* <dl>
* <dt>{@code android:shortcutId}</dt>
- * <dd>Mandatory shortcut ID.
- * <p>
- * This must be a string literal.
- * A resource string, such as <code>@string/foo</code>, cannot be used.
+ * <dd><p>A string literal, which represents the shortcut when a {@code ShortcutManager} object
+ * performs operations on it.</p>
+ * <p class="note"><b>Note: </b>You cannot set this attribute's value to a resource string, such
+ * as <code>@string/foo</code>.</p>
* </dd>
*
* <dt>{@code android:enabled}</dt>
- * <dd>Default is {@code true}. Can be set to {@code false} in order
- * to disable a static shortcut that was published in a previous version and set a custom
- * disabled message. If a custom disabled message is not needed, then a static shortcut can
- * be simply removed from the XML file rather than keeping it with {@code enabled="false"}.</dd>
+ * <dd><p>Whether the user can interact with the shortcut from a supported launcher.</p>
+ * <p>The default value is {@code true}. If you set it to {@code false}, you should also set
+ * {@code android:shortcutDisabledMessage} to a message that explains why you've disabled the
+ * shortcut. If you don't think you need to provide such a message, it's easiest to just remove
+ * the shortcut from the XML file entirely, rather than changing the values of the shortcut's
+ * {@code android:enabled} and {@code android:shortcutDisabledMessage} attributes.
+ * </dd>
*
* <dt>{@code android:icon}</dt>
- * <dd>Shortcut icon.</dd>
+ * <dd><p>The <a href="/topic/performance/graphics/index.html">bitmap</a> or
+ * <a href="/guide/practices/ui_guidelines/icon_design_adaptive.html">adaptive icon</a> that the
+ * launcher uses when displaying the shortcut to the user. This value can be either the path to an
+ * image or the resource file that contains the image. Use adaptive icons whenever possible to
+ * improve performance and consistency.</p>
+ * <p class="note"><b>Note: </b>Shortcut icons cannot include
+ * <a href="/training/material/drawables.html#DrawableTint">tints</a>.
+ * </dd>
*
* <dt>{@code android:shortcutShortLabel}</dt>
- * <dd>Mandatory shortcut short label.
- * See {@link ShortcutInfo.Builder#setShortLabel(CharSequence)}.
- * <p>
- * This must be a resource string, such as <code>@string/shortcut_label</code>.
+ * <dd><p>A concise phrase that describes the shortcut's purpose. For more information, see
+ * {@link ShortcutInfo.Builder#setShortLabel(CharSequence)}.</p>
+ * <p class="note"><b>Note: </b>This attribute's value must be a resource string, such as
+ * <code>@string/shortcut_short_label</code>.</p>
* </dd>
*
* <dt>{@code android:shortcutLongLabel}</dt>
- * <dd>Shortcut long label.
- * See {@link ShortcutInfo.Builder#setLongLabel(CharSequence)}.
- * <p>
- * This must be a resource string, such as <code>@string/shortcut_long_label</code>.
+ * <dd><p>An extended phrase that describes the shortcut's purpose. If there's enough space, the
+ * launcher displays this value instead of {@code android:shortcutShortLabel}. For more
+ * information, see {@link ShortcutInfo.Builder#setLongLabel(CharSequence)}.</p>
+ * <p class="note"><b>Note: </b>This attribute's value must be a resource string, such as
+ * <code>@string/shortcut_long_label</code>.</p>
* </dd>
*
* <dt>{@code android:shortcutDisabledMessage}</dt>
- * <dd>When {@code android:enabled} is set to
- * {@code false}, this attribute is used to display a custom disabled message.
- * <p>
- * This must be a resource string, such as <code>@string/shortcut_disabled_message</code>.
+ * <dd><p>The message that appears in a supported launcher when the user attempts to launch a
+ * disabled shortcut. The message should explain to the user why the shortcut is now disabled.
+ * This attribute's value has no effect if {@code android:enabled} is {@code true}.</p>
+ * <p class="note"><b>Note: </b>This attribute's value must be a resource string, such as
+ * <code>@string/shortcut_disabled_message</code>.</p>
* </dd>
+ * </dl>
+ *
+ * <h3>Inner elements that define static shortcuts</h3>
+ *
+ * <p>The XML file that lists an app's static shortcuts supports the following elements inside each
+ * {@code <shortcut>} element. You must include an {@code intent} inner element for each
+ * static shortcut that you define.</p>
*
+ * <dl>
* <dt>{@code intent}</dt>
- * <dd>Intent to launch when the user selects the shortcut.
- * {@code android:action} is mandatory.
- * See <a href="{@docRoot}guide/topics/ui/settings.html#Intents">Using intents</a> for the
- * other supported tags.
- * <p>You can provide multiple intents for a single shortcut so that the last defined activity is
- * launched with the other activities in the
+ * <dd><p>The action that the system launches when the user selects the shortcut. This intent must
+ * provide a value for the {@code android:action} attribute.</p>
+ * <p>You can provide multiple intents for a single shortcut. If you do so, the last defined
+ * activity is launched, and the other activities are placed in the
* <a href="/guide/components/tasks-and-back-stack.html">back stack</a>. See
- * {@link android.app.TaskStackBuilder} for details.
- * <p><b>Note:</b> String resources may not be used within an {@code <intent>} element.
+ * <a href="/guide/topics/ui/shortcuts.html#static">Using Static Shortcuts</a> and the
+ * {@link android.app.TaskStackBuilder} class reference for details.</p>
+ * <p class="note"><b>Note:</b> This {@code intent} element cannot include string resources.</p>
+ * <p>To learn more about how to configure intents, see
+ * <a href="{@docRoot}guide/topics/ui/settings.html#Intents">Using intents</a>.</p>
* </dd>
+ *
* <dt>{@code categories}</dt>
- * <dd>Specify shortcut categories. Currently only
- * {@link ShortcutInfo#SHORTCUT_CATEGORY_CONVERSATION} is defined in the framework.
+ * <dd><p>Provides a grouping for the types of actions that your app's shortcuts perform, such as
+ * creating new chat messages.</p>
+ * <p>For a list of supported shortcut categories, see the {@link ShortcutInfo} class reference
+ * for a list of supported shortcut categories.
* </dd>
* </dl>
*
* <h3>Updating shortcuts</h3>
*
+ * <p>Each app's launcher icon can contain at most {@link #getMaxShortcutCountPerActivity()} number
+ * of static and dynamic shortcuts combined. There is no limit to the number of pinned shortcuts
+ * that an app can create, though.
+ *
+ * <p>When a dynamic shortcut is pinned, even when the publisher removes it as a dynamic shortcut,
+ * the pinned shortcut is still visible and launchable. This allows an app to have more than
+ * {@link #getMaxShortcutCountPerActivity()} number of shortcuts.
+ *
* <p>As an example, suppose {@link #getMaxShortcutCountPerActivity()} is 5:
* <ol>
* <li>A chat app publishes 5 dynamic shortcuts for the 5 most recent
@@ -168,18 +219,13 @@ import java.util.List;
*
* <li>The user pins all 5 of the shortcuts.
*
- * <li>Later, the user has started 3 additional conversations (c6, c7, and c8),
- * so the publisher app
- * re-publishes its dynamic shortcuts. The new dynamic shortcut list is:
- * c4, c5, ..., c8.
- * The publisher app has to remove c1, c2, and c3 because it can't have more than
- * 5 dynamic shortcuts.
- *
- * <li>However, even though c1, c2, and c3 are no longer dynamic shortcuts, the pinned
- * shortcuts for these conversations are still available and launchable.
- *
- * <li>At this point, the user can access a total of 8 shortcuts that link to activities in
- * the publisher app, including the 3 pinned shortcuts, even though an app can have at most 5
+ * <li>Later, the user has started 3 additional conversations (c6, c7, and c8), so the publisher
+ * app re-publishes its dynamic shortcuts. The new dynamic shortcut list is: c4, c5, ..., c8.
+ * <p>The publisher app has to remove c1, c2, and c3 because it can't have more than 5 dynamic
+ * shortcuts. However, c1, c2, and c3 are still pinned shortcuts that the user can access and
+ * launch.
+ * <p>At this point, the user can access a total of 8 shortcuts that link to activities in the
+ * publisher app, including the 3 pinned shortcuts, even though an app can have at most 5
* dynamic shortcuts.
*
* <li>The app can use {@link #updateShortcuts(List)} to update <em>any</em> of the existing
@@ -196,44 +242,23 @@ import java.util.List;
* Dynamic shortcuts can be published with any set of {@link Intent#addFlags Intent} flags.
* Typically, {@link Intent#FLAG_ACTIVITY_CLEAR_TASK} is specified, possibly along with other
* flags; otherwise, if the app is already running, the app is simply brought to
- * the foreground, and the target activity may not appear.
+ * the foreground, and the target activity might not appear.
*
* <p>Static shortcuts <b>cannot</b> have custom intent flags.
* The first intent of a static shortcut will always have {@link Intent#FLAG_ACTIVITY_NEW_TASK}
* and {@link Intent#FLAG_ACTIVITY_CLEAR_TASK} set. This means, when the app is already running, all
- * the existing activities in your app will be destroyed when a static shortcut is launched.
+ * the existing activities in your app are destroyed when a static shortcut is launched.
* If this behavior is not desirable, you can use a <em>trampoline activity</em>, or an invisible
* activity that starts another activity in {@link Activity#onCreate}, then calls
* {@link Activity#finish()}:
* <ol>
* <li>In the <code>AndroidManifest.xml</code> file, the trampoline activity should include the
* attribute assignment {@code android:taskAffinity=""}.
- * <li>In the shortcuts resource file, the intent within the static shortcut should point at
+ * <li>In the shortcuts resource file, the intent within the static shortcut should reference
* the trampoline activity.
* </ol>
*
- * <h3>Handling system locale changes</h3>
- *
- * <p>Apps should update dynamic and pinned shortcuts when the system locale changes using the
- * {@link Intent#ACTION_LOCALE_CHANGED} broadcast. When the system locale changes,
- * <a href="/guide/topics/ui/shortcuts.html#rate-limit">rate limiting</a> is reset, so even
- * background apps can add and update dynamic shortcuts until the rate limit is reached again.
- *
- * <h3>Shortcut limits</h3>
- *
- * <p>Only main activities&mdash;activities that handle the {@code MAIN} action and the
- * {@code LAUNCHER} category&mdash;can have shortcuts. If an app has multiple main activities, you
- * need to define the set of shortcuts for <em>each</em> activity.
- *
- * <p>Each launcher icon can have at most {@link #getMaxShortcutCountPerActivity()} number of
- * static and dynamic shortcuts combined. There is no limit to the number of pinned shortcuts that
- * an app can create.
- *
- * <p>When a dynamic shortcut is pinned, even when the publisher removes it as a dynamic shortcut,
- * the pinned shortcut is still visible and launchable. This allows an app to have more than
- * {@link #getMaxShortcutCountPerActivity()} number of shortcuts.
- *
- * <h4>Rate limiting</h4>
+ * <h3>Rate limiting</h3>
*
* <p>When <a href="/guide/topics/ui/shortcuts.html#rate-limit">rate limiting</a> is active,
* {@link #isRateLimitingActive()} returns {@code true}.
@@ -243,8 +268,20 @@ import java.util.List;
* <ul>
* <li>An app comes to the foreground.
* <li>The system locale changes.
- * <li>The user performs the <strong>inline reply</strong> action on a notification.
+ * <li>The user performs the <a href="/guide/topics/ui/notifiers/notifications.html#direct">inline
+ * reply</a> action on a notification.
* </ul>
+ *
+ * <h3>Handling system locale changes</h3>
+ *
+ * <p>Apps should update dynamic and pinned shortcuts when they receive the
+ * {@link Intent#ACTION_LOCALE_CHANGED} broadcast, indicating that the system locale has changed.
+ * <p>When the system locale changes, <a href="/guide/topics/ui/shortcuts.html#rate-limit">rate
+ * limiting</a> is reset, so even background apps can add and update dynamic shortcuts until the
+ * rate limit is reached again.
+ *
+ * <h3>Retrieving class instances</h3>
+ * <!-- Provides a heading for the content filled in by the @SystemService annotation below -->
*/
@SystemService(Context.SHORTCUT_SERVICE)
public class ShortcutManager {
@@ -733,6 +770,6 @@ public class ShortcutManager {
/** @hide injection point */
@VisibleForTesting
protected int injectMyUserId() {
- return UserHandle.myUserId();
+ return mContext.getUserId();
}
}
diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java
index 7b7d8ae42528..e6f682d22b14 100644
--- a/core/java/android/content/pm/ShortcutServiceInternal.java
+++ b/core/java/android/content/pm/ShortcutServiceInternal.java
@@ -46,7 +46,7 @@ public abstract class ShortcutServiceInternal {
@NonNull String callingPackage, long changedSince,
@Nullable String packageName, @Nullable List<String> shortcutIds,
@Nullable ComponentName componentName, @ShortcutQuery.QueryFlags int flags,
- int userId);
+ int userId, int callingPid, int callingUid);
public abstract boolean
isPinnedByCaller(int launcherUserId, @NonNull String callingPackage,
@@ -58,7 +58,8 @@ public abstract class ShortcutServiceInternal {
public abstract Intent[] createShortcutIntents(
int launcherUserId, @NonNull String callingPackage,
- @NonNull String packageName, @NonNull String shortcutId, int userId);
+ @NonNull String packageName, @NonNull String shortcutId, int userId,
+ int callingPid, int callingUid);
public abstract void addListener(@NonNull ShortcutChangeListener listener);
@@ -70,11 +71,17 @@ public abstract class ShortcutServiceInternal {
@NonNull String packageName, @NonNull String shortcutId, int userId);
public abstract boolean hasShortcutHostPermission(int launcherUserId,
- @NonNull String callingPackage);
+ @NonNull String callingPackage, int callingPid, int callingUid);
+
+ public abstract void setShortcutHostPackage(@NonNull String type, @Nullable String packageName,
+ int userId);
public abstract boolean requestPinAppWidget(@NonNull String callingPackage,
@NonNull AppWidgetProviderInfo appWidget, @Nullable Bundle extras,
@Nullable IntentSender resultIntent, int userId);
public abstract boolean isRequestPinItemSupported(int callingUserId, int requestType);
+
+ public abstract boolean isForegroundDefaultLauncher(@NonNull String callingPackage,
+ int callingUid);
}
diff --git a/core/java/android/content/pm/Signature.java b/core/java/android/content/pm/Signature.java
index fdc54aedac96..a2a14eddd59f 100644
--- a/core/java/android/content/pm/Signature.java
+++ b/core/java/android/content/pm/Signature.java
@@ -285,6 +285,29 @@ public class Signature implements Parcelable {
}
/**
+ * Test if given {@link Signature} objects are effectively equal. In rare
+ * cases, certificates can have slightly malformed encoding which causes
+ * exact-byte checks to fail.
+ * <p>
+ * To identify effective equality, we bounce the certificates through an
+ * decode/encode pass before doing the exact-byte check. To reduce attack
+ * surface area, we only allow a byte size delta of a few bytes.
+ *
+ * @throws CertificateException if the before/after length differs
+ * substantially, usually a signal of something fishy going on.
+ * @hide
+ */
+ public static boolean areEffectiveMatch(Signature a, Signature b)
+ throws CertificateException {
+ final CertificateFactory cf = CertificateFactory.getInstance("X.509");
+
+ final Signature aPrime = bounce(cf, a);
+ final Signature bPrime = bounce(cf, b);
+
+ return aPrime.equals(bPrime);
+ }
+
+ /**
* Bounce the given {@link Signature} through a decode/encode cycle.
*
* @throws CertificateException if the before/after length differs
diff --git a/core/java/android/content/pm/SigningInfo.java b/core/java/android/content/pm/SigningInfo.java
new file mode 100644
index 000000000000..ef8740359627
--- /dev/null
+++ b/core/java/android/content/pm/SigningInfo.java
@@ -0,0 +1,139 @@
+/*
+ * 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.
+ */
+
+package android.content.pm;
+
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Information pertaining to the signing certificates used to sign a package.
+ */
+public final class SigningInfo implements Parcelable {
+
+ @NonNull
+ private final PackageParser.SigningDetails mSigningDetails;
+
+ public SigningInfo() {
+ mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
+ }
+
+ /**
+ * @hide only packagemanager should be populating this
+ */
+ public SigningInfo(PackageParser.SigningDetails signingDetails) {
+ mSigningDetails = new PackageParser.SigningDetails(signingDetails);
+ }
+
+ public SigningInfo(SigningInfo orig) {
+ mSigningDetails = new PackageParser.SigningDetails(orig.mSigningDetails);
+ }
+
+ private SigningInfo(Parcel source) {
+ mSigningDetails = PackageParser.SigningDetails.CREATOR.createFromParcel(source);
+ }
+
+ /**
+ * Although relatively uncommon, packages may be signed by more than one signer, in which case
+ * their identity is viewed as being the set of all signers, not just any one.
+ */
+ public boolean hasMultipleSigners() {
+ return mSigningDetails.signatures != null && mSigningDetails.signatures.length > 1;
+ }
+
+ /**
+ * APK Signature Scheme v3 enables packages to provide a proof-of-rotation record that the
+ * platform verifies, and uses, to allow the use of new signing certificates. This is only
+ * available to packages that are not signed by multiple signers. In the event of a change to a
+ * new signing certificate, the package's past signing certificates are presented as well. Any
+ * check of a package's signing certificate should also include a search through its entire
+ * signing history, since it could change to a new signing certificate at any time.
+ */
+ public boolean hasPastSigningCertificates() {
+ return mSigningDetails.signatures != null
+ && mSigningDetails.pastSigningCertificates != null;
+ }
+
+ /**
+ * Returns the signing certificates this package has proven it is authorized to use. This
+ * includes both the signing certificate associated with the signer of the package and the past
+ * signing certificates it included as its proof of signing certificate rotation. This method
+ * is the preferred replacement for the {@code GET_SIGNATURES} flag used with {@link
+ * PackageManager#getPackageInfo(String, int)}. When determining if a package is signed by a
+ * desired certificate, the returned array should be checked to determine if it is one of the
+ * entries.
+ *
+ * <note>
+ * This method returns null if the package is signed by multiple signing certificates, as
+ * opposed to being signed by one current signer and also providing the history of past
+ * signing certificates. {@link #hasMultipleSigners()} may be used to determine if this
+ * package is signed by multiple signers. Packages which are signed by multiple signers
+ * cannot change their signing certificates and their {@code Signature} array should be
+ * checked to make sure that every entry matches the looked-for signing certificates.
+ * </note>
+ */
+ public Signature[] getSigningCertificateHistory() {
+ if (hasMultipleSigners()) {
+ return null;
+ } else if (!hasPastSigningCertificates()) {
+
+ // this package is only signed by one signer with no history, return it
+ return mSigningDetails.signatures;
+ } else {
+
+ // this package has provided proof of past signing certificates, include them
+ return mSigningDetails.pastSigningCertificates;
+ }
+ }
+
+ /**
+ * Returns the signing certificates used to sign the APK contents of this application. Not
+ * including any past signing certificates the package proved it is authorized to use.
+ * <note>
+ * This method should not be used unless {@link #hasMultipleSigners()} returns true,
+ * indicating that {@link #getSigningCertificateHistory()} cannot be used, otherwise {@link
+ * #getSigningCertificateHistory()} should be preferred.
+ * </note>
+ */
+ public Signature[] getApkContentsSigners() {
+ return mSigningDetails.signatures;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int parcelableFlags) {
+ mSigningDetails.writeToParcel(dest, parcelableFlags);
+ }
+
+ public static final Parcelable.Creator<SigningInfo> CREATOR =
+ new Parcelable.Creator<SigningInfo>() {
+ @Override
+ public SigningInfo createFromParcel(Parcel source) {
+ return new SigningInfo(source);
+ }
+
+ @Override
+ public SigningInfo[] newArray(int size) {
+ return new SigningInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/content/pm/VerifierDeviceIdentity.java b/core/java/android/content/pm/VerifierDeviceIdentity.java
index a8cdb6ae8af6..90be6f316ce6 100644
--- a/core/java/android/content/pm/VerifierDeviceIdentity.java
+++ b/core/java/android/content/pm/VerifierDeviceIdentity.java
@@ -19,6 +19,8 @@ package android.content.pm;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.UnsupportedEncodingException;
import java.security.SecureRandom;
import java.util.Random;
@@ -86,6 +88,7 @@ public class VerifierDeviceIdentity implements Parcelable {
* @return verifier device identity based on the input from the provided
* random number generator
*/
+ @VisibleForTesting
static VerifierDeviceIdentity generate(Random rng) {
long identity = rng.nextLong();
return new VerifierDeviceIdentity(identity);
diff --git a/core/java/android/content/pm/VersionedPackage.java b/core/java/android/content/pm/VersionedPackage.java
index 29c5efe7c77a..395346641b1e 100644
--- a/core/java/android/content/pm/VersionedPackage.java
+++ b/core/java/android/content/pm/VersionedPackage.java
@@ -28,7 +28,7 @@ import java.lang.annotation.RetentionPolicy;
*/
public final class VersionedPackage implements Parcelable {
private final String mPackageName;
- private final int mVersionCode;
+ private final long mVersionCode;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -47,9 +47,21 @@ public final class VersionedPackage implements Parcelable {
mVersionCode = versionCode;
}
+ /**
+ * Creates a new instance. Use {@link PackageManager#VERSION_CODE_HIGHEST}
+ * to refer to the highest version code of this package.
+ * @param packageName The package name.
+ * @param versionCode The version code.
+ */
+ public VersionedPackage(@NonNull String packageName,
+ @VersionCode long versionCode) {
+ mPackageName = packageName;
+ mVersionCode = versionCode;
+ }
+
private VersionedPackage(Parcel parcel) {
mPackageName = parcel.readString();
- mVersionCode = parcel.readInt();
+ mVersionCode = parcel.readLong();
}
/**
@@ -62,11 +74,19 @@ public final class VersionedPackage implements Parcelable {
}
/**
+ * @deprecated use {@link #getLongVersionCode()} instead.
+ */
+ @Deprecated
+ public @VersionCode int getVersionCode() {
+ return (int) (mVersionCode & 0x7fffffff);
+ }
+
+ /**
* Gets the version code.
*
* @return The version code.
*/
- public @VersionCode int getVersionCode() {
+ public @VersionCode long getLongVersionCode() {
return mVersionCode;
}
@@ -83,7 +103,7 @@ public final class VersionedPackage implements Parcelable {
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeString(mPackageName);
- parcel.writeInt(mVersionCode);
+ parcel.writeLong(mVersionCode);
}
public static final Creator<VersionedPackage> CREATOR = new Creator<VersionedPackage>() {
diff --git a/core/java/android/content/pm/dex/ArtManager.java b/core/java/android/content/pm/dex/ArtManager.java
index 075306366264..b0970f4878db 100644
--- a/core/java/android/content/pm/dex/ArtManager.java
+++ b/core/java/android/content/pm/dex/ArtManager.java
@@ -16,11 +16,16 @@
package android.content.pm.dex;
+import static android.Manifest.permission.PACKAGE_USAGE_STATS;
+import static android.Manifest.permission.READ_RUNTIME_PROFILES;
+
+import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.content.Context;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
@@ -61,13 +66,14 @@ public class ArtManager {
@Retention(RetentionPolicy.SOURCE)
public @interface ProfileType {}
-
- private IArtManager mArtManager;
+ private final Context mContext;
+ private final IArtManager mArtManager;
/**
* @hide
*/
- public ArtManager(@NonNull IArtManager manager) {
+ public ArtManager(@NonNull Context context, @NonNull IArtManager manager) {
+ mContext = context;
mArtManager = manager;
}
@@ -98,18 +104,19 @@ public class ArtManager {
* @param callback the callback which should be used for the result
* @param executor the executor which should be used to post the result
*/
- @RequiresPermission(android.Manifest.permission.READ_RUNTIME_PROFILES)
+ @RequiresPermission(allOf = { READ_RUNTIME_PROFILES, PACKAGE_USAGE_STATS })
public void snapshotRuntimeProfile(@ProfileType int profileType, @Nullable String packageName,
- @Nullable String codePath, @NonNull Executor executor,
+ @Nullable String codePath, @NonNull @CallbackExecutor Executor executor,
@NonNull SnapshotRuntimeProfileCallback callback) {
Slog.d(TAG, "Requesting profile snapshot for " + packageName + ":" + codePath);
SnapshotRuntimeProfileCallbackDelegate delegate =
new SnapshotRuntimeProfileCallbackDelegate(callback, executor);
try {
- mArtManager.snapshotRuntimeProfile(profileType, packageName, codePath, delegate);
+ mArtManager.snapshotRuntimeProfile(profileType, packageName, codePath, delegate,
+ mContext.getOpPackageName());
} catch (RemoteException e) {
- e.rethrowAsRuntimeException();
+ throw e.rethrowAsRuntimeException();
}
}
@@ -121,14 +128,13 @@ public class ArtManager {
* @param profileType can be either {@link ArtManager#PROFILE_APPS}
* or {@link ArtManager#PROFILE_BOOT_IMAGE}
*/
- @RequiresPermission(android.Manifest.permission.READ_RUNTIME_PROFILES)
+ @RequiresPermission(allOf = { READ_RUNTIME_PROFILES, PACKAGE_USAGE_STATS })
public boolean isRuntimeProfilingEnabled(@ProfileType int profileType) {
try {
- return mArtManager.isRuntimeProfilingEnabled(profileType);
+ return mArtManager.isRuntimeProfilingEnabled(profileType, mContext.getOpPackageName());
} catch (RemoteException e) {
- e.rethrowAsRuntimeException();
+ throw e.rethrowAsRuntimeException();
}
- return false;
}
/**
diff --git a/core/java/android/content/pm/dex/ArtManagerInternal.java b/core/java/android/content/pm/dex/ArtManagerInternal.java
new file mode 100644
index 000000000000..62ab9e02f858
--- /dev/null
+++ b/core/java/android/content/pm/dex/ArtManagerInternal.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright 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.
+ */
+
+package android.content.pm.dex;
+
+import android.content.pm.ApplicationInfo;
+
+/**
+ * Art manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class ArtManagerInternal {
+
+ /**
+ * Return optimization information about the application {@code info} when
+ * in executes using the specified {@code abi}.
+ */
+ public abstract PackageOptimizationInfo getPackageOptimizationInfo(
+ ApplicationInfo info, String abi);
+}
diff --git a/core/java/android/content/pm/dex/IArtManager.aidl b/core/java/android/content/pm/dex/IArtManager.aidl
index 6abfdbab6e26..7f0de7ec09b4 100644
--- a/core/java/android/content/pm/dex/IArtManager.aidl
+++ b/core/java/android/content/pm/dex/IArtManager.aidl
@@ -44,8 +44,8 @@ interface IArtManager {
* {@link ArtManager#isRuntimeProfilingEnabled(int)} does not return true for the given
* {@code profileType}.
*/
- oneway void snapshotRuntimeProfile(int profileType, in String packageName,
- in String codePath, in ISnapshotRuntimeProfileCallback callback);
+ void snapshotRuntimeProfile(int profileType, in String packageName,
+ in String codePath, in ISnapshotRuntimeProfileCallback callback, String callingPackage);
/**
* Returns true if runtime profiles are enabled for the given type, false otherwise.
@@ -54,5 +54,5 @@ interface IArtManager {
*
* @param profileType
*/
- boolean isRuntimeProfilingEnabled(int profileType);
+ boolean isRuntimeProfilingEnabled(int profileType, String callingPackage);
}
diff --git a/core/java/android/content/pm/dex/PackageOptimizationInfo.java b/core/java/android/content/pm/dex/PackageOptimizationInfo.java
new file mode 100644
index 000000000000..7e7d29e68dd9
--- /dev/null
+++ b/core/java/android/content/pm/dex/PackageOptimizationInfo.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright 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.
+ */
+
+package android.content.pm.dex;
+
+/**
+ * Encapsulates information about the optimizations performed on a package.
+ *
+ * @hide
+ */
+public class PackageOptimizationInfo {
+ private final int mCompilationFilter;
+ private final int mCompilationReason;
+
+ public PackageOptimizationInfo(int compilerFilter, int compilationReason) {
+ this.mCompilationReason = compilationReason;
+ this.mCompilationFilter = compilerFilter;
+ }
+
+ public int getCompilationReason() {
+ return mCompilationReason;
+ }
+
+ public int getCompilationFilter() {
+ return mCompilationFilter;
+ }
+
+ /**
+ * Create a default optimization info object for the case when we have no information.
+ */
+ public static PackageOptimizationInfo createWithNoInfo() {
+ return new PackageOptimizationInfo(-1, -1);
+ }
+}
diff --git a/core/java/android/content/pm/permission/RuntimePermissionPresenter.java b/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
index 02d0a6d8bd36..79bc9a30b1e2 100644
--- a/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
+++ b/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
@@ -274,6 +274,7 @@ public final class RuntimePermissionPresenter {
}
}
+ @GuardedBy("mLock")
private void scheduleNextMessageIfNeededLocked() {
if (mBound && mRemoteInstance != null && !mPendingWork.isEmpty()) {
Message nextMessage = mPendingWork.remove(0);
diff --git a/core/java/android/content/pm/split/DefaultSplitAssetLoader.java b/core/java/android/content/pm/split/DefaultSplitAssetLoader.java
index 99eb4702d32e..9e3a8f48996c 100644
--- a/core/java/android/content/pm/split/DefaultSplitAssetLoader.java
+++ b/core/java/android/content/pm/split/DefaultSplitAssetLoader.java
@@ -15,10 +15,13 @@
*/
package android.content.pm.split;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
+import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.PackageParser.ParseFlags;
+import android.content.res.ApkAssets;
import android.content.res.AssetManager;
import android.os.Build;
@@ -26,6 +29,8 @@ import com.android.internal.util.ArrayUtils;
import libcore.io.IoUtils;
+import java.io.IOException;
+
/**
* Loads the base and split APKs into a single AssetManager.
* @hide
@@ -33,68 +38,66 @@ import libcore.io.IoUtils;
public class DefaultSplitAssetLoader implements SplitAssetLoader {
private final String mBaseCodePath;
private final String[] mSplitCodePaths;
- private final int mFlags;
-
+ private final @ParseFlags int mFlags;
private AssetManager mCachedAssetManager;
- public DefaultSplitAssetLoader(PackageParser.PackageLite pkg, int flags) {
+ public DefaultSplitAssetLoader(PackageParser.PackageLite pkg, @ParseFlags int flags) {
mBaseCodePath = pkg.baseCodePath;
mSplitCodePaths = pkg.splitCodePaths;
mFlags = flags;
}
- private static void loadApkIntoAssetManager(AssetManager assets, String apkPath, int flags)
- throws PackageParser.PackageParserException {
- if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(apkPath)) {
- throw new PackageParser.PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
- "Invalid package file: " + apkPath);
+ private static ApkAssets loadApkAssets(String path, @ParseFlags int flags)
+ throws PackageParserException {
+ if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(path)) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+ "Invalid package file: " + path);
}
- if (assets.addAssetPath(apkPath) == 0) {
- throw new PackageParser.PackageParserException(
- INSTALL_PARSE_FAILED_BAD_MANIFEST,
- "Failed adding asset path: " + apkPath);
+ try {
+ return ApkAssets.loadFromPath(path);
+ } catch (IOException e) {
+ throw new PackageParserException(INSTALL_FAILED_INVALID_APK,
+ "Failed to load APK at path " + path, e);
}
}
@Override
- public AssetManager getBaseAssetManager() throws PackageParser.PackageParserException {
+ public AssetManager getBaseAssetManager() throws PackageParserException {
if (mCachedAssetManager != null) {
return mCachedAssetManager;
}
- AssetManager assets = new AssetManager();
- try {
- assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- Build.VERSION.RESOURCES_SDK_INT);
- loadApkIntoAssetManager(assets, mBaseCodePath, mFlags);
-
- if (!ArrayUtils.isEmpty(mSplitCodePaths)) {
- for (String apkPath : mSplitCodePaths) {
- loadApkIntoAssetManager(assets, apkPath, mFlags);
- }
- }
+ ApkAssets[] apkAssets = new ApkAssets[(mSplitCodePaths != null
+ ? mSplitCodePaths.length : 0) + 1];
- mCachedAssetManager = assets;
- assets = null;
- return mCachedAssetManager;
- } finally {
- if (assets != null) {
- IoUtils.closeQuietly(assets);
+ // Load the base.
+ int splitIdx = 0;
+ apkAssets[splitIdx++] = loadApkAssets(mBaseCodePath, mFlags);
+
+ // Load any splits.
+ if (!ArrayUtils.isEmpty(mSplitCodePaths)) {
+ for (String apkPath : mSplitCodePaths) {
+ apkAssets[splitIdx++] = loadApkAssets(apkPath, mFlags);
}
}
+
+ AssetManager assets = new AssetManager();
+ assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ Build.VERSION.RESOURCES_SDK_INT);
+ assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
+
+ mCachedAssetManager = assets;
+ return mCachedAssetManager;
}
@Override
- public AssetManager getSplitAssetManager(int splitIdx)
- throws PackageParser.PackageParserException {
+ public AssetManager getSplitAssetManager(int splitIdx) throws PackageParserException {
return getBaseAssetManager();
}
@Override
public void close() throws Exception {
- if (mCachedAssetManager != null) {
- IoUtils.closeQuietly(mCachedAssetManager);
- }
+ IoUtils.closeQuietly(mCachedAssetManager);
}
}
diff --git a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
index 16023f0d9d97..58eaabfa62f2 100644
--- a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
+++ b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
@@ -15,17 +15,21 @@
*/
package android.content.pm.split;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
import android.annotation.NonNull;
+import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.PackageParser.ParseFlags;
+import android.content.res.ApkAssets;
import android.content.res.AssetManager;
import android.os.Build;
import android.util.SparseArray;
import libcore.io.IoUtils;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
@@ -34,17 +38,15 @@ import java.util.Collections;
* is to be used when an application opts-in to isolated split loading.
* @hide
*/
-public class SplitAssetDependencyLoader
- extends SplitDependencyLoader<PackageParser.PackageParserException>
+public class SplitAssetDependencyLoader extends SplitDependencyLoader<PackageParserException>
implements SplitAssetLoader {
private final String[] mSplitPaths;
- private final int mFlags;
-
- private String[][] mCachedPaths;
- private AssetManager[] mCachedAssetManagers;
+ private final @ParseFlags int mFlags;
+ private final ApkAssets[][] mCachedSplitApks;
+ private final AssetManager[] mCachedAssetManagers;
public SplitAssetDependencyLoader(PackageParser.PackageLite pkg,
- SparseArray<int[]> dependencies, int flags) {
+ SparseArray<int[]> dependencies, @ParseFlags int flags) {
super(dependencies);
// The base is inserted into index 0, so we need to shift all the splits by 1.
@@ -53,7 +55,7 @@ public class SplitAssetDependencyLoader
System.arraycopy(pkg.splitCodePaths, 0, mSplitPaths, 1, pkg.splitCodePaths.length);
mFlags = flags;
- mCachedPaths = new String[mSplitPaths.length][];
+ mCachedSplitApks = new ApkAssets[mSplitPaths.length][];
mCachedAssetManagers = new AssetManager[mSplitPaths.length];
}
@@ -62,58 +64,60 @@ public class SplitAssetDependencyLoader
return mCachedAssetManagers[splitIdx] != null;
}
- private static AssetManager createAssetManagerWithPaths(String[] assetPaths, int flags)
- throws PackageParser.PackageParserException {
- final AssetManager assets = new AssetManager();
+ private static ApkAssets loadApkAssets(String path, @ParseFlags int flags)
+ throws PackageParserException {
+ if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(path)) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+ "Invalid package file: " + path);
+ }
+
try {
- assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- Build.VERSION.RESOURCES_SDK_INT);
-
- for (String assetPath : assetPaths) {
- if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 &&
- !PackageParser.isApkPath(assetPath)) {
- throw new PackageParser.PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
- "Invalid package file: " + assetPath);
- }
-
- if (assets.addAssetPath(assetPath) == 0) {
- throw new PackageParser.PackageParserException(
- INSTALL_PARSE_FAILED_BAD_MANIFEST,
- "Failed adding asset path: " + assetPath);
- }
- }
- return assets;
- } catch (Throwable e) {
- IoUtils.closeQuietly(assets);
- throw e;
+ return ApkAssets.loadFromPath(path);
+ } catch (IOException e) {
+ throw new PackageParserException(PackageManager.INSTALL_FAILED_INVALID_APK,
+ "Failed to load APK at path " + path, e);
}
}
+ private static AssetManager createAssetManagerWithAssets(ApkAssets[] apkAssets) {
+ final AssetManager assets = new AssetManager();
+ assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ Build.VERSION.RESOURCES_SDK_INT);
+ assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
+ return assets;
+ }
+
@Override
protected void constructSplit(int splitIdx, @NonNull int[] configSplitIndices,
- int parentSplitIdx) throws PackageParser.PackageParserException {
- final ArrayList<String> assetPaths = new ArrayList<>();
+ int parentSplitIdx) throws PackageParserException {
+ final ArrayList<ApkAssets> assets = new ArrayList<>();
+
+ // Include parent ApkAssets.
if (parentSplitIdx >= 0) {
- Collections.addAll(assetPaths, mCachedPaths[parentSplitIdx]);
+ Collections.addAll(assets, mCachedSplitApks[parentSplitIdx]);
}
- assetPaths.add(mSplitPaths[splitIdx]);
+ // Include this ApkAssets.
+ assets.add(loadApkAssets(mSplitPaths[splitIdx], mFlags));
+
+ // Load and include all config splits for this feature.
for (int configSplitIdx : configSplitIndices) {
- assetPaths.add(mSplitPaths[configSplitIdx]);
+ assets.add(loadApkAssets(mSplitPaths[configSplitIdx], mFlags));
}
- mCachedPaths[splitIdx] = assetPaths.toArray(new String[assetPaths.size()]);
- mCachedAssetManagers[splitIdx] = createAssetManagerWithPaths(mCachedPaths[splitIdx],
- mFlags);
+
+ // Cache the results.
+ mCachedSplitApks[splitIdx] = assets.toArray(new ApkAssets[assets.size()]);
+ mCachedAssetManagers[splitIdx] = createAssetManagerWithAssets(mCachedSplitApks[splitIdx]);
}
@Override
- public AssetManager getBaseAssetManager() throws PackageParser.PackageParserException {
+ public AssetManager getBaseAssetManager() throws PackageParserException {
loadDependenciesForSplit(0);
return mCachedAssetManagers[0];
}
@Override
- public AssetManager getSplitAssetManager(int idx) throws PackageParser.PackageParserException {
+ public AssetManager getSplitAssetManager(int idx) throws PackageParserException {
// Since we insert the base at position 0, and PackageParser keeps splits separate from
// the base, we need to adjust the index.
loadDependenciesForSplit(idx + 1);
diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java
new file mode 100644
index 000000000000..9de8be3e86af
--- /dev/null
+++ b/core/java/android/content/res/ApkAssets.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.res;
+
+import android.annotation.NonNull;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+
+/**
+ * The loaded, immutable, in-memory representation of an APK.
+ *
+ * The main implementation is native C++ and there is very little API surface exposed here. The APK
+ * is mainly accessed via {@link AssetManager}.
+ *
+ * Since the ApkAssets instance is immutable, it can be reused and shared across AssetManagers,
+ * making the creation of AssetManagers very cheap.
+ * @hide
+ */
+public final class ApkAssets {
+ @GuardedBy("this") private final long mNativePtr;
+ @GuardedBy("this") private StringBlock mStringBlock;
+
+ /**
+ * Creates a new ApkAssets instance from the given path on disk.
+ *
+ * @param path The path to an APK on disk.
+ * @return a new instance of ApkAssets.
+ * @throws IOException if a disk I/O error or parsing error occurred.
+ */
+ public static @NonNull ApkAssets loadFromPath(@NonNull String path) throws IOException {
+ return new ApkAssets(path, false /*system*/, false /*forceSharedLib*/, false /*overlay*/);
+ }
+
+ /**
+ * Creates a new ApkAssets instance from the given path on disk.
+ *
+ * @param path The path to an APK on disk.
+ * @param system When true, the APK is loaded as a system APK (framework).
+ * @return a new instance of ApkAssets.
+ * @throws IOException if a disk I/O error or parsing error occurred.
+ */
+ public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system)
+ throws IOException {
+ return new ApkAssets(path, system, false /*forceSharedLib*/, false /*overlay*/);
+ }
+
+ /**
+ * Creates a new ApkAssets instance from the given path on disk.
+ *
+ * @param path The path to an APK on disk.
+ * @param system When true, the APK is loaded as a system APK (framework).
+ * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are
+ * loaded as a shared library.
+ * @return a new instance of ApkAssets.
+ * @throws IOException if a disk I/O error or parsing error occurred.
+ */
+ public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system,
+ boolean forceSharedLibrary) throws IOException {
+ return new ApkAssets(path, system, forceSharedLibrary, false /*overlay*/);
+ }
+
+ /**
+ * Creates a new ApkAssets instance from the given file descriptor. Not for use by applications.
+ *
+ * Performs a dup of the underlying fd, so you must take care of still closing
+ * the FileDescriptor yourself (and can do that whenever you want).
+ *
+ * @param fd The FileDescriptor of an open, readable APK.
+ * @param friendlyName The friendly name used to identify this ApkAssets when logging.
+ * @param system When true, the APK is loaded as a system APK (framework).
+ * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are
+ * loaded as a shared library.
+ * @return a new instance of ApkAssets.
+ * @throws IOException if a disk I/O error or parsing error occurred.
+ */
+ public static @NonNull ApkAssets loadFromFd(@NonNull FileDescriptor fd,
+ @NonNull String friendlyName, boolean system, boolean forceSharedLibrary)
+ throws IOException {
+ return new ApkAssets(fd, friendlyName, system, forceSharedLibrary);
+ }
+
+ /**
+ * Creates a new ApkAssets instance from the IDMAP at idmapPath. The overlay APK path
+ * is encoded within the IDMAP.
+ *
+ * @param idmapPath Path to the IDMAP of an overlay APK.
+ * @param system When true, the APK is loaded as a system APK (framework).
+ * @return a new instance of ApkAssets.
+ * @throws IOException if a disk I/O error or parsing error occurred.
+ */
+ public static @NonNull ApkAssets loadOverlayFromPath(@NonNull String idmapPath, boolean system)
+ throws IOException {
+ return new ApkAssets(idmapPath, system, false /*forceSharedLibrary*/, true /*overlay*/);
+ }
+
+ private ApkAssets(@NonNull String path, boolean system, boolean forceSharedLib, boolean overlay)
+ throws IOException {
+ Preconditions.checkNotNull(path, "path");
+ mNativePtr = nativeLoad(path, system, forceSharedLib, overlay);
+ mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
+ }
+
+ private ApkAssets(@NonNull FileDescriptor fd, @NonNull String friendlyName, boolean system,
+ boolean forceSharedLib) throws IOException {
+ Preconditions.checkNotNull(fd, "fd");
+ Preconditions.checkNotNull(friendlyName, "friendlyName");
+ mNativePtr = nativeLoadFromFd(fd, friendlyName, system, forceSharedLib);
+ mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
+ }
+
+ public @NonNull String getAssetPath() {
+ synchronized (this) {
+ return nativeGetAssetPath(mNativePtr);
+ }
+ }
+
+ CharSequence getStringFromPool(int idx) {
+ synchronized (this) {
+ return mStringBlock.get(idx);
+ }
+ }
+
+ /**
+ * Retrieve a parser for a compiled XML file. This is associated with a single APK and
+ * <em>NOT</em> a full AssetManager. This means that shared-library references will not be
+ * dynamically assigned runtime package IDs.
+ *
+ * @param fileName The path to the file within the APK.
+ * @return An XmlResourceParser.
+ * @throws IOException if the file was not found or an error occurred retrieving it.
+ */
+ public @NonNull XmlResourceParser openXml(@NonNull String fileName) throws IOException {
+ Preconditions.checkNotNull(fileName, "fileName");
+ synchronized (this) {
+ long nativeXmlPtr = nativeOpenXml(mNativePtr, fileName);
+ try (XmlBlock block = new XmlBlock(null, nativeXmlPtr)) {
+ XmlResourceParser parser = block.newParser();
+ // If nativeOpenXml doesn't throw, it will always return a valid native pointer,
+ // which makes newParser always return non-null. But let's be paranoid.
+ if (parser == null) {
+ throw new AssertionError("block.newParser() returned a null parser");
+ }
+ return parser;
+ }
+ }
+ }
+
+ /**
+ * Returns false if the underlying APK was changed since this ApkAssets was loaded.
+ */
+ public boolean isUpToDate() {
+ synchronized (this) {
+ return nativeIsUpToDate(mNativePtr);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "ApkAssets{path=" + getAssetPath() + "}";
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ nativeDestroy(mNativePtr);
+ }
+
+ private static native long nativeLoad(
+ @NonNull String path, boolean system, boolean forceSharedLib, boolean overlay)
+ throws IOException;
+ private static native long nativeLoadFromFd(@NonNull FileDescriptor fd,
+ @NonNull String friendlyName, boolean system, boolean forceSharedLib)
+ throws IOException;
+ private static native void nativeDestroy(long ptr);
+ private static native @NonNull String nativeGetAssetPath(long ptr);
+ private static native long nativeGetStringBlock(long ptr);
+ private static native boolean nativeIsUpToDate(long ptr);
+ private static native long nativeOpenXml(long ptr, @NonNull String fileName) throws IOException;
+}
diff --git a/core/java/android/content/res/AssetFileDescriptor.java b/core/java/android/content/res/AssetFileDescriptor.java
index 28edde0250cf..be4103622b11 100644
--- a/core/java/android/content/res/AssetFileDescriptor.java
+++ b/core/java/android/content/res/AssetFileDescriptor.java
@@ -195,7 +195,7 @@ public class AssetFileDescriptor implements Parcelable, Closeable {
/**
* An InputStream you can create on a ParcelFileDescriptor, which will
* take care of calling {@link ParcelFileDescriptor#close
- * ParcelFileDescritor.close()} for you when the stream is closed.
+ * ParcelFileDescriptor.close()} for you when the stream is closed.
*/
public static class AutoCloseInputStream
extends ParcelFileDescriptor.AutoCloseInputStream {
@@ -282,7 +282,7 @@ public class AssetFileDescriptor implements Parcelable, Closeable {
/**
* An OutputStream you can create on a ParcelFileDescriptor, which will
* take care of calling {@link ParcelFileDescriptor#close
- * ParcelFileDescritor.close()} for you when the stream is closed.
+ * ParcelFileDescriptor.close()} for you when the stream is closed.
*/
public static class AutoCloseOutputStream
extends ParcelFileDescriptor.AutoCloseOutputStream {
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 97284fb1e635..289534273d13 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -18,21 +18,33 @@ package android.content.res;
import android.annotation.AnyRes;
import android.annotation.ArrayRes;
+import android.annotation.AttrRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringRes;
+import android.annotation.StyleRes;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration.NativeConfig;
import android.os.ParcelFileDescriptor;
+import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
import android.util.TypedValue;
-import dalvik.annotation.optimization.FastNative;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+import libcore.io.IoUtils;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.channels.FileLock;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
/**
@@ -43,7 +55,20 @@ import java.util.HashMap;
* bytes.
*/
public final class AssetManager implements AutoCloseable {
- /* modes used when opening an asset */
+ private static final String TAG = "AssetManager";
+ private static final boolean DEBUG_REFS = false;
+
+ private static final String FRAMEWORK_APK_PATH = "/system/framework/framework-res.apk";
+
+ private static final Object sSync = new Object();
+
+ private static final ApkAssets[] sEmptyApkAssets = new ApkAssets[0];
+
+ // Not private for LayoutLib's BridgeAssetManager.
+ @GuardedBy("sSync") static AssetManager sSystem = null;
+
+ @GuardedBy("sSync") private static ApkAssets[] sSystemApkAssets = new ApkAssets[0];
+ @GuardedBy("sSync") private static ArraySet<ApkAssets> sSystemApkAssetsSet;
/**
* Mode for {@link #open(String, int)}: no specific information about how
@@ -66,87 +91,391 @@ public final class AssetManager implements AutoCloseable {
*/
public static final int ACCESS_BUFFER = 3;
- private static final String TAG = "AssetManager";
- private static final boolean localLOGV = false;
-
- private static final boolean DEBUG_REFS = false;
-
- private static final Object sSync = new Object();
- /*package*/ static AssetManager sSystem = null;
+ @GuardedBy("this") private final TypedValue mValue = new TypedValue();
+ @GuardedBy("this") private final long[] mOffsets = new long[2];
- private final TypedValue mValue = new TypedValue();
- private final long[] mOffsets = new long[2];
-
- // For communication with native code.
- private long mObject;
+ // Pointer to native implementation, stuffed inside a long.
+ @GuardedBy("this") private long mObject;
+
+ // The loaded asset paths.
+ @GuardedBy("this") private ApkAssets[] mApkAssets;
+
+ // Debug/reference counting implementation.
+ @GuardedBy("this") private boolean mOpen = true;
+ @GuardedBy("this") private int mNumRefs = 1;
+ @GuardedBy("this") private HashMap<Long, RuntimeException> mRefStacks;
+
+ /**
+ * A Builder class that helps create an AssetManager with only a single invocation of
+ * {@link AssetManager#setApkAssets(ApkAssets[], boolean)}. Without using this builder,
+ * AssetManager must ensure there are system ApkAssets loaded at all times, which when combined
+ * with the user's call to add additional ApkAssets, results in multiple calls to
+ * {@link AssetManager#setApkAssets(ApkAssets[], boolean)}.
+ * @hide
+ */
+ public static class Builder {
+ private ArrayList<ApkAssets> mUserApkAssets = new ArrayList<>();
+
+ public Builder addApkAssets(ApkAssets apkAssets) {
+ mUserApkAssets.add(apkAssets);
+ return this;
+ }
+
+ public AssetManager build() {
+ // Retrieving the system ApkAssets forces their creation as well.
+ final ApkAssets[] systemApkAssets = getSystem().getApkAssets();
+
+ final int totalApkAssetCount = systemApkAssets.length + mUserApkAssets.size();
+ final ApkAssets[] apkAssets = new ApkAssets[totalApkAssetCount];
+
+ System.arraycopy(systemApkAssets, 0, apkAssets, 0, systemApkAssets.length);
+
+ final int userApkAssetCount = mUserApkAssets.size();
+ for (int i = 0; i < userApkAssetCount; i++) {
+ apkAssets[i + systemApkAssets.length] = mUserApkAssets.get(i);
+ }
+
+ // Calling this constructor prevents creation of system ApkAssets, which we took care
+ // of in this Builder.
+ final AssetManager assetManager = new AssetManager(false /*sentinel*/);
+ assetManager.mApkAssets = apkAssets;
+ AssetManager.nativeSetApkAssets(assetManager.mObject, apkAssets,
+ false /*invalidateCaches*/);
+ return assetManager;
+ }
+ }
- private StringBlock mStringBlocks[] = null;
-
- private int mNumRefs = 1;
- private boolean mOpen = true;
- private HashMap<Long, RuntimeException> mRefStacks;
-
/**
* Create a new AssetManager containing only the basic system assets.
* Applications will not generally use this method, instead retrieving the
* appropriate asset manager with {@link Resources#getAssets}. Not for
* use by applications.
- * {@hide}
+ * @hide
*/
public AssetManager() {
- synchronized (this) {
- if (DEBUG_REFS) {
- mNumRefs = 0;
- incRefsLocked(this.hashCode());
- }
- init(false);
- if (localLOGV) Log.v(TAG, "New asset manager: " + this);
- ensureSystemAssets();
+ final ApkAssets[] assets;
+ synchronized (sSync) {
+ createSystemAssetsInZygoteLocked();
+ assets = sSystemApkAssets;
}
- }
- private static void ensureSystemAssets() {
- synchronized (sSync) {
- if (sSystem == null) {
- AssetManager system = new AssetManager(true);
- system.makeStringBlocks(null);
- sSystem = system;
- }
+ mObject = nativeCreate();
+ if (DEBUG_REFS) {
+ mNumRefs = 0;
+ incRefsLocked(hashCode());
}
+
+ // Always set the framework resources.
+ setApkAssets(assets, false /*invalidateCaches*/);
}
-
- private AssetManager(boolean isSystem) {
+
+ /**
+ * Private constructor that doesn't call ensureSystemAssets.
+ * Used for the creation of system assets.
+ */
+ @SuppressWarnings("unused")
+ private AssetManager(boolean sentinel) {
+ mObject = nativeCreate();
if (DEBUG_REFS) {
- synchronized (this) {
- mNumRefs = 0;
- incRefsLocked(this.hashCode());
+ mNumRefs = 0;
+ incRefsLocked(hashCode());
+ }
+ }
+
+ /**
+ * This must be called from Zygote so that system assets are shared by all applications.
+ */
+ @GuardedBy("sSync")
+ private static void createSystemAssetsInZygoteLocked() {
+ if (sSystem != null) {
+ return;
+ }
+
+ // Make sure that all IDMAPs are up to date.
+ nativeVerifySystemIdmaps();
+
+ try {
+ final ArrayList<ApkAssets> apkAssets = new ArrayList<>();
+ apkAssets.add(ApkAssets.loadFromPath(FRAMEWORK_APK_PATH, true /*system*/));
+ loadStaticRuntimeOverlays(apkAssets);
+
+ sSystemApkAssetsSet = new ArraySet<>(apkAssets);
+ sSystemApkAssets = apkAssets.toArray(new ApkAssets[apkAssets.size()]);
+ sSystem = new AssetManager(true /*sentinel*/);
+ sSystem.setApkAssets(sSystemApkAssets, false /*invalidateCaches*/);
+ } catch (IOException e) {
+ throw new IllegalStateException("Failed to create system AssetManager", e);
+ }
+ }
+
+ /**
+ * Loads the static runtime overlays declared in /data/resource-cache/overlays.list.
+ * Throws an exception if the file is corrupt or if loading the APKs referenced by the file
+ * fails. Returns quietly if the overlays.list file doesn't exist.
+ * @param outApkAssets The list to fill with the loaded ApkAssets.
+ */
+ private static void loadStaticRuntimeOverlays(ArrayList<ApkAssets> outApkAssets)
+ throws IOException {
+ final FileInputStream fis;
+ try {
+ fis = new FileInputStream("/data/resource-cache/overlays.list");
+ } catch (FileNotFoundException e) {
+ // We might not have any overlays, this is fine. We catch here since ApkAssets
+ // loading can also fail with the same exception, which we would want to propagate.
+ Log.i(TAG, "no overlays.list file found");
+ return;
+ }
+
+ try {
+ // Acquire a lock so that any idmap scanning doesn't impact the current set.
+ // The order of this try-with-resources block matters. We must release the lock, and
+ // then close the file streams when exiting the block.
+ try (final BufferedReader br = new BufferedReader(new InputStreamReader(fis));
+ final FileLock flock = fis.getChannel().lock(0, Long.MAX_VALUE, true /*shared*/)) {
+ for (String line; (line = br.readLine()) != null; ) {
+ final String idmapPath = line.split(" ")[1];
+ outApkAssets.add(ApkAssets.loadOverlayFromPath(idmapPath, true /*system*/));
+ }
}
+ } finally {
+ // When BufferedReader is closed above, FileInputStream is closed as well. But let's be
+ // paranoid.
+ IoUtils.closeQuietly(fis);
}
- init(true);
- if (localLOGV) Log.v(TAG, "New asset manager: " + this);
}
/**
* Return a global shared asset manager that provides access to only
* system assets (no application assets).
- * {@hide}
+ * @hide
*/
public static AssetManager getSystem() {
- ensureSystemAssets();
- return sSystem;
+ synchronized (sSync) {
+ createSystemAssetsInZygoteLocked();
+ return sSystem;
+ }
}
/**
* Close this asset manager.
*/
+ @Override
public void close() {
- synchronized(this) {
- //System.out.println("Release: num=" + mNumRefs
- // + ", released=" + mReleased);
+ synchronized (this) {
+ if (!mOpen) {
+ return;
+ }
+
+ mOpen = false;
+ decRefsLocked(hashCode());
+ }
+ }
+
+ /**
+ * Changes the asset paths in this AssetManager. This replaces the {@link #addAssetPath(String)}
+ * family of methods.
+ *
+ * @param apkAssets The new set of paths.
+ * @param invalidateCaches Whether to invalidate any caches. This should almost always be true.
+ * Set this to false if you are appending new resources
+ * (not new configurations).
+ * @hide
+ */
+ public void setApkAssets(@NonNull ApkAssets[] apkAssets, boolean invalidateCaches) {
+ Preconditions.checkNotNull(apkAssets, "apkAssets");
+
+ ApkAssets[] newApkAssets = new ApkAssets[sSystemApkAssets.length + apkAssets.length];
+
+ // Copy the system assets first.
+ System.arraycopy(sSystemApkAssets, 0, newApkAssets, 0, sSystemApkAssets.length);
+
+ // Copy the given ApkAssets if they are not already in the system list.
+ int newLength = sSystemApkAssets.length;
+ for (ApkAssets apkAsset : apkAssets) {
+ if (!sSystemApkAssetsSet.contains(apkAsset)) {
+ newApkAssets[newLength++] = apkAsset;
+ }
+ }
+
+ // Truncate if necessary.
+ if (newLength != newApkAssets.length) {
+ newApkAssets = Arrays.copyOf(newApkAssets, newLength);
+ }
+
+ synchronized (this) {
+ ensureOpenLocked();
+ mApkAssets = newApkAssets;
+ nativeSetApkAssets(mObject, mApkAssets, invalidateCaches);
+ if (invalidateCaches) {
+ // Invalidate all caches.
+ invalidateCachesLocked(-1);
+ }
+ }
+ }
+
+ /**
+ * Invalidates the caches in this AssetManager according to the bitmask `diff`.
+ *
+ * @param diff The bitmask of changes generated by {@link Configuration#diff(Configuration)}.
+ * @see ActivityInfo.Config
+ */
+ private void invalidateCachesLocked(int diff) {
+ // TODO(adamlesinski): Currently there are no caches to invalidate in Java code.
+ }
+
+ /**
+ * Returns the set of ApkAssets loaded by this AssetManager. If the AssetManager is closed, this
+ * returns a 0-length array.
+ * @hide
+ */
+ public @NonNull ApkAssets[] getApkAssets() {
+ synchronized (this) {
if (mOpen) {
- mOpen = false;
- decRefsLocked(this.hashCode());
+ return mApkAssets;
+ }
+ }
+ return sEmptyApkAssets;
+ }
+
+ /**
+ * Returns a cookie for use with the other APIs of AssetManager.
+ * @return 0 if the path was not found, otherwise a positive integer cookie representing
+ * this path in the AssetManager.
+ * @hide
+ */
+ public int findCookieForPath(@NonNull String path) {
+ Preconditions.checkNotNull(path, "path");
+ synchronized (this) {
+ ensureValidLocked();
+ final int count = mApkAssets.length;
+ for (int i = 0; i < count; i++) {
+ if (path.equals(mApkAssets[i].getAssetPath())) {
+ return i + 1;
+ }
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)}
+ * @hide
+ */
+ @Deprecated
+ public int addAssetPath(String path) {
+ return addAssetPathInternal(path, false /*overlay*/, false /*appAsLib*/);
+ }
+
+ /**
+ * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)}
+ * @hide
+ */
+ @Deprecated
+ public int addAssetPathAsSharedLibrary(String path) {
+ return addAssetPathInternal(path, false /*overlay*/, true /*appAsLib*/);
+ }
+
+ /**
+ * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)}
+ * @hide
+ */
+ @Deprecated
+ public int addOverlayPath(String path) {
+ return addAssetPathInternal(path, true /*overlay*/, false /*appAsLib*/);
+ }
+
+ private int addAssetPathInternal(String path, boolean overlay, boolean appAsLib) {
+ Preconditions.checkNotNull(path, "path");
+ synchronized (this) {
+ ensureOpenLocked();
+ final int count = mApkAssets.length;
+
+ // See if we already have it loaded.
+ for (int i = 0; i < count; i++) {
+ if (mApkAssets[i].getAssetPath().equals(path)) {
+ return i + 1;
+ }
+ }
+
+ final ApkAssets assets;
+ try {
+ if (overlay) {
+ // TODO(b/70343104): This hardcoded path will be removed once
+ // addAssetPathInternal is deleted.
+ final String idmapPath = "/data/resource-cache/"
+ + path.substring(1).replace('/', '@')
+ + "@idmap";
+ assets = ApkAssets.loadOverlayFromPath(idmapPath, false /*system*/);
+ } else {
+ assets = ApkAssets.loadFromPath(path, false /*system*/, appAsLib);
+ }
+ } catch (IOException e) {
+ return 0;
+ }
+
+ mApkAssets = Arrays.copyOf(mApkAssets, count + 1);
+ mApkAssets[count] = assets;
+ nativeSetApkAssets(mObject, mApkAssets, true);
+ invalidateCachesLocked(-1);
+ return count + 1;
+ }
+ }
+
+ /**
+ * Ensures that the native implementation has not been destroyed.
+ * The AssetManager may have been closed, but references to it still exist
+ * and therefore the native implementation is not destroyed.
+ */
+ @GuardedBy("this")
+ private void ensureValidLocked() {
+ if (mObject == 0) {
+ throw new RuntimeException("AssetManager has been destroyed");
+ }
+ }
+
+ /**
+ * Ensures that the AssetManager has not been explicitly closed. If this method passes,
+ * then this implies that ensureValidLocked() also passes.
+ */
+ @GuardedBy("this")
+ private void ensureOpenLocked() {
+ // If mOpen is true, this implies that mObject != 0.
+ if (!mOpen) {
+ throw new RuntimeException("AssetManager has been closed");
+ }
+ }
+
+ /**
+ * Populates {@code outValue} with the data associated a particular
+ * resource identifier for the current configuration.
+ *
+ * @param resId the resource identifier to load
+ * @param densityDpi the density bucket for which to load the resource
+ * @param outValue the typed value in which to put the data
+ * @param resolveRefs {@code true} to resolve references, {@code false}
+ * to leave them unresolved
+ * @return {@code true} if the data was loaded into {@code outValue},
+ * {@code false} otherwise
+ */
+ boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue,
+ boolean resolveRefs) {
+ Preconditions.checkNotNull(outValue, "outValue");
+ synchronized (this) {
+ ensureValidLocked();
+ final int cookie = nativeGetResourceValue(
+ mObject, resId, (short) densityDpi, outValue, resolveRefs);
+ if (cookie <= 0) {
+ return false;
+ }
+
+ // Convert the changing configurations flags populated by native code.
+ outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
+ outValue.changingConfigurations);
+
+ if (outValue.type == TypedValue.TYPE_STRING) {
+ outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data);
}
+ return true;
}
}
@@ -157,8 +486,7 @@ public final class AssetManager implements AutoCloseable {
* @param resId the resource identifier to load
* @return the string value, or {@code null}
*/
- @Nullable
- final CharSequence getResourceText(@StringRes int resId) {
+ @Nullable CharSequence getResourceText(@StringRes int resId) {
synchronized (this) {
final TypedValue outValue = mValue;
if (getResourceValue(resId, 0, outValue, true)) {
@@ -173,15 +501,15 @@ public final class AssetManager implements AutoCloseable {
* identifier for the current configuration.
*
* @param resId the resource identifier to load
- * @param bagEntryId
+ * @param bagEntryId the index into the bag to load
* @return the string value, or {@code null}
*/
- @Nullable
- final CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) {
+ @Nullable CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) {
synchronized (this) {
+ ensureValidLocked();
final TypedValue outValue = mValue;
- final int block = loadResourceBagValue(resId, bagEntryId, outValue, true);
- if (block < 0) {
+ final int cookie = nativeGetResourceBagValue(mObject, resId, bagEntryId, outValue);
+ if (cookie <= 0) {
return null;
}
@@ -190,52 +518,60 @@ public final class AssetManager implements AutoCloseable {
outValue.changingConfigurations);
if (outValue.type == TypedValue.TYPE_STRING) {
- return mStringBlocks[block].get(outValue.data);
+ return mApkAssets[cookie - 1].getStringFromPool(outValue.data);
}
return outValue.coerceToString();
}
}
+ int getResourceArraySize(@ArrayRes int resId) {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetResourceArraySize(mObject, resId);
+ }
+ }
+
/**
- * Retrieves the string array associated with a particular resource
- * identifier for the current configuration.
+ * Populates `outData` with array elements of `resId`. `outData` is normally
+ * used with
+ * {@link TypedArray}.
*
- * @param resId the resource identifier of the string array
- * @return the string array, or {@code null}
+ * Each logical element in `outData` is {@link TypedArray#STYLE_NUM_ENTRIES}
+ * long,
+ * with the indices of the data representing the type, value, asset cookie,
+ * resource ID,
+ * configuration change mask, and density of the element.
+ *
+ * @param resId The resource ID of an array resource.
+ * @param outData The array to populate with data.
+ * @return The length of the array.
+ *
+ * @see TypedArray#STYLE_TYPE
+ * @see TypedArray#STYLE_DATA
+ * @see TypedArray#STYLE_ASSET_COOKIE
+ * @see TypedArray#STYLE_RESOURCE_ID
+ * @see TypedArray#STYLE_CHANGING_CONFIGURATIONS
+ * @see TypedArray#STYLE_DENSITY
*/
- @Nullable
- final String[] getResourceStringArray(@ArrayRes int resId) {
- return getArrayStringResource(resId);
+ int getResourceArray(@ArrayRes int resId, @NonNull int[] outData) {
+ Preconditions.checkNotNull(outData, "outData");
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetResourceArray(mObject, resId, outData);
+ }
}
/**
- * Populates {@code outValue} with the data associated a particular
- * resource identifier for the current configuration.
+ * Retrieves the string array associated with a particular resource
+ * identifier for the current configuration.
*
- * @param resId the resource identifier to load
- * @param densityDpi the density bucket for which to load the resource
- * @param outValue the typed value in which to put the data
- * @param resolveRefs {@code true} to resolve references, {@code false}
- * to leave them unresolved
- * @return {@code true} if the data was loaded into {@code outValue},
- * {@code false} otherwise
+ * @param resId the resource identifier of the string array
+ * @return the string array, or {@code null}
*/
- final boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue,
- boolean resolveRefs) {
+ @Nullable String[] getResourceStringArray(@ArrayRes int resId) {
synchronized (this) {
- final int block = loadResourceValue(resId, (short) densityDpi, outValue, resolveRefs);
- if (block < 0) {
- return false;
- }
-
- // Convert the changing configurations flags populated by native code.
- outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
- outValue.changingConfigurations);
-
- if (outValue.type == TypedValue.TYPE_STRING) {
- outValue.string = mStringBlocks[block].get(outValue.data);
- }
- return true;
+ ensureValidLocked();
+ return nativeGetResourceStringArray(mObject, resId);
}
}
@@ -245,26 +581,48 @@ public final class AssetManager implements AutoCloseable {
*
* @param resId the resource id of the string array
*/
- final @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) {
+ @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) {
synchronized (this) {
- final int[] rawInfoArray = getArrayStringInfo(resId);
+ ensureValidLocked();
+ final int[] rawInfoArray = nativeGetResourceStringArrayInfo(mObject, resId);
if (rawInfoArray == null) {
return null;
}
+
final int rawInfoArrayLen = rawInfoArray.length;
final int infoArrayLen = rawInfoArrayLen / 2;
- int block;
- int index;
final CharSequence[] retArray = new CharSequence[infoArrayLen];
for (int i = 0, j = 0; i < rawInfoArrayLen; i = i + 2, j++) {
- block = rawInfoArray[i];
- index = rawInfoArray[i + 1];
- retArray[j] = index >= 0 ? mStringBlocks[block].get(index) : null;
+ int cookie = rawInfoArray[i];
+ int index = rawInfoArray[i + 1];
+ retArray[j] = (index >= 0 && cookie > 0)
+ ? mApkAssets[cookie - 1].getStringFromPool(index) : null;
}
return retArray;
}
}
+ @Nullable int[] getResourceIntArray(@ArrayRes int resId) {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetResourceIntArray(mObject, resId);
+ }
+ }
+
+ /**
+ * Get the attributes for a style resource. These are the &lt;item&gt;
+ * elements in
+ * a &lt;style&gt; resource.
+ * @param resId The resource ID of the style
+ * @return An array of attribute IDs.
+ */
+ @AttrRes int[] getStyleAttributes(@StyleRes int resId) {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetStyleAttributes(mObject, resId);
+ }
+ }
+
/**
* Populates {@code outValue} with the data associated with a particular
* resource identifier for the current configuration. Resolves theme
@@ -278,73 +636,88 @@ public final class AssetManager implements AutoCloseable {
* @return {@code true} if the data was loaded into {@code outValue},
* {@code false} otherwise
*/
- final boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue,
+ boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue,
boolean resolveRefs) {
- final int block = loadThemeAttributeValue(theme, resId, outValue, resolveRefs);
- if (block < 0) {
- return false;
+ Preconditions.checkNotNull(outValue, "outValue");
+ synchronized (this) {
+ ensureValidLocked();
+ final int cookie = nativeThemeGetAttributeValue(mObject, theme, resId, outValue,
+ resolveRefs);
+ if (cookie <= 0) {
+ return false;
+ }
+
+ // Convert the changing configurations flags populated by native code.
+ outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
+ outValue.changingConfigurations);
+
+ if (outValue.type == TypedValue.TYPE_STRING) {
+ outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data);
+ }
+ return true;
}
+ }
- // Convert the changing configurations flags populated by native code.
- outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
- outValue.changingConfigurations);
+ void dumpTheme(long theme, int priority, String tag, String prefix) {
+ synchronized (this) {
+ ensureValidLocked();
+ nativeThemeDump(mObject, theme, priority, tag, prefix);
+ }
+ }
- if (outValue.type == TypedValue.TYPE_STRING) {
- final StringBlock[] blocks = ensureStringBlocks();
- outValue.string = blocks[block].get(outValue.data);
+ @Nullable String getResourceName(@AnyRes int resId) {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetResourceName(mObject, resId);
}
- return true;
}
- /**
- * Ensures the string blocks are loaded.
- *
- * @return the string blocks
- */
- @NonNull
- final StringBlock[] ensureStringBlocks() {
+ @Nullable String getResourcePackageName(@AnyRes int resId) {
synchronized (this) {
- if (mStringBlocks == null) {
- makeStringBlocks(sSystem.mStringBlocks);
- }
- return mStringBlocks;
+ ensureValidLocked();
+ return nativeGetResourcePackageName(mObject, resId);
}
}
- /*package*/ final void makeStringBlocks(StringBlock[] seed) {
- final int seedNum = (seed != null) ? seed.length : 0;
- final int num = getStringBlockCount();
- mStringBlocks = new StringBlock[num];
- if (localLOGV) Log.v(TAG, "Making string blocks for " + this
- + ": " + num);
- for (int i=0; i<num; i++) {
- if (i < seedNum) {
- mStringBlocks[i] = seed[i];
- } else {
- mStringBlocks[i] = new StringBlock(getNativeStringBlock(i), true);
- }
+ @Nullable String getResourceTypeName(@AnyRes int resId) {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetResourceTypeName(mObject, resId);
}
}
- /*package*/ final CharSequence getPooledStringForCookie(int cookie, int id) {
+ @Nullable String getResourceEntryName(@AnyRes int resId) {
synchronized (this) {
- // Cookies map to string blocks starting at 1.
- return mStringBlocks[cookie - 1].get(id);
+ ensureValidLocked();
+ return nativeGetResourceEntryName(mObject, resId);
}
}
+ @AnyRes int getResourceIdentifier(@NonNull String name, @Nullable String defType,
+ @Nullable String defPackage) {
+ synchronized (this) {
+ ensureValidLocked();
+ // name is checked in JNI.
+ return nativeGetResourceIdentifier(mObject, name, defType, defPackage);
+ }
+ }
+
+ CharSequence getPooledStringForCookie(int cookie, int id) {
+ // Cookies map to ApkAssets starting at 1.
+ return getApkAssets()[cookie - 1].getStringFromPool(id);
+ }
+
/**
* Open an asset using ACCESS_STREAMING mode. This provides access to
* files that have been bundled with an application as assets -- that is,
* files placed in to the "assets" directory.
*
- * @param fileName The name of the asset to open. This name can be
- * hierarchical.
+ * @param fileName The name of the asset to open. This name can be hierarchical.
*
* @see #open(String, int)
* @see #list
*/
- public final InputStream open(String fileName) throws IOException {
+ public @NonNull InputStream open(@NonNull String fileName) throws IOException {
return open(fileName, ACCESS_STREAMING);
}
@@ -354,8 +727,7 @@ public final class AssetManager implements AutoCloseable {
* with an application as assets -- that is, files placed in to the
* "assets" directory.
*
- * @param fileName The name of the asset to open. This name can be
- * hierarchical.
+ * @param fileName The name of the asset to open. This name can be hierarchical.
* @param accessMode Desired access mode for retrieving the data.
*
* @see #ACCESS_UNKNOWN
@@ -365,34 +737,40 @@ public final class AssetManager implements AutoCloseable {
* @see #open(String)
* @see #list
*/
- public final InputStream open(String fileName, int accessMode)
- throws IOException {
+ public @NonNull InputStream open(@NonNull String fileName, int accessMode) throws IOException {
+ Preconditions.checkNotNull(fileName, "fileName");
synchronized (this) {
- if (!mOpen) {
- throw new RuntimeException("Assetmanager has been closed");
- }
- long asset = openAsset(fileName, accessMode);
- if (asset != 0) {
- AssetInputStream res = new AssetInputStream(asset);
- incRefsLocked(res.hashCode());
- return res;
+ ensureOpenLocked();
+ final long asset = nativeOpenAsset(mObject, fileName, accessMode);
+ if (asset == 0) {
+ throw new FileNotFoundException("Asset file: " + fileName);
}
+ final AssetInputStream assetInputStream = new AssetInputStream(asset);
+ incRefsLocked(assetInputStream.hashCode());
+ return assetInputStream;
}
- throw new FileNotFoundException("Asset file: " + fileName);
}
- public final AssetFileDescriptor openFd(String fileName)
- throws IOException {
+ /**
+ * Open an uncompressed asset by mmapping it and returning an {@link AssetFileDescriptor}.
+ * This provides access to files that have been bundled with an application as assets -- that
+ * is, files placed in to the "assets" directory.
+ *
+ * The asset must be uncompressed, or an exception will be thrown.
+ *
+ * @param fileName The name of the asset to open. This name can be hierarchical.
+ * @return An open AssetFileDescriptor.
+ */
+ public @NonNull AssetFileDescriptor openFd(@NonNull String fileName) throws IOException {
+ Preconditions.checkNotNull(fileName, "fileName");
synchronized (this) {
- if (!mOpen) {
- throw new RuntimeException("Assetmanager has been closed");
- }
- ParcelFileDescriptor pfd = openAssetFd(fileName, mOffsets);
- if (pfd != null) {
- return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
+ ensureOpenLocked();
+ final ParcelFileDescriptor pfd = nativeOpenAssetFd(mObject, fileName, mOffsets);
+ if (pfd == null) {
+ throw new FileNotFoundException("Asset file: " + fileName);
}
+ return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
}
- throw new FileNotFoundException("Asset file: " + fileName);
}
/**
@@ -407,90 +785,121 @@ public final class AssetManager implements AutoCloseable {
*
* @see #open
*/
- public native final String[] list(String path)
- throws IOException;
+ public @Nullable String[] list(@NonNull String path) throws IOException {
+ Preconditions.checkNotNull(path, "path");
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeList(mObject, path);
+ }
+ }
/**
- * {@hide}
* Open a non-asset file as an asset using ACCESS_STREAMING mode. This
* provides direct access to all of the files included in an application
* package (not only its assets). Applications should not normally use
* this.
- *
+ *
+ * @param fileName Name of the asset to retrieve.
+ *
* @see #open(String)
+ * @hide
*/
- public final InputStream openNonAsset(String fileName) throws IOException {
+ public @NonNull InputStream openNonAsset(@NonNull String fileName) throws IOException {
return openNonAsset(0, fileName, ACCESS_STREAMING);
}
/**
- * {@hide}
* Open a non-asset file as an asset using a specific access mode. This
* provides direct access to all of the files included in an application
* package (not only its assets). Applications should not normally use
* this.
- *
+ *
+ * @param fileName Name of the asset to retrieve.
+ * @param accessMode Desired access mode for retrieving the data.
+ *
+ * @see #ACCESS_UNKNOWN
+ * @see #ACCESS_STREAMING
+ * @see #ACCESS_RANDOM
+ * @see #ACCESS_BUFFER
* @see #open(String, int)
+ * @hide
*/
- public final InputStream openNonAsset(String fileName, int accessMode)
- throws IOException {
+ public @NonNull InputStream openNonAsset(@NonNull String fileName, int accessMode)
+ throws IOException {
return openNonAsset(0, fileName, accessMode);
}
/**
- * {@hide}
* Open a non-asset in a specified package. Not for use by applications.
- *
+ *
* @param cookie Identifier of the package to be opened.
* @param fileName Name of the asset to retrieve.
+ * @hide
*/
- public final InputStream openNonAsset(int cookie, String fileName)
- throws IOException {
+ public @NonNull InputStream openNonAsset(int cookie, @NonNull String fileName)
+ throws IOException {
return openNonAsset(cookie, fileName, ACCESS_STREAMING);
}
/**
- * {@hide}
* Open a non-asset in a specified package. Not for use by applications.
- *
+ *
* @param cookie Identifier of the package to be opened.
* @param fileName Name of the asset to retrieve.
* @param accessMode Desired access mode for retrieving the data.
+ * @hide
*/
- public final InputStream openNonAsset(int cookie, String fileName, int accessMode)
- throws IOException {
+ public @NonNull InputStream openNonAsset(int cookie, @NonNull String fileName, int accessMode)
+ throws IOException {
+ Preconditions.checkNotNull(fileName, "fileName");
synchronized (this) {
- if (!mOpen) {
- throw new RuntimeException("Assetmanager has been closed");
- }
- long asset = openNonAssetNative(cookie, fileName, accessMode);
- if (asset != 0) {
- AssetInputStream res = new AssetInputStream(asset);
- incRefsLocked(res.hashCode());
- return res;
+ ensureOpenLocked();
+ final long asset = nativeOpenNonAsset(mObject, cookie, fileName, accessMode);
+ if (asset == 0) {
+ throw new FileNotFoundException("Asset absolute file: " + fileName);
}
+ final AssetInputStream assetInputStream = new AssetInputStream(asset);
+ incRefsLocked(assetInputStream.hashCode());
+ return assetInputStream;
}
- throw new FileNotFoundException("Asset absolute file: " + fileName);
}
- public final AssetFileDescriptor openNonAssetFd(String fileName)
+ /**
+ * Open a non-asset as an asset by mmapping it and returning an {@link AssetFileDescriptor}.
+ * This provides direct access to all of the files included in an application
+ * package (not only its assets). Applications should not normally use this.
+ *
+ * The asset must not be compressed, or an exception will be thrown.
+ *
+ * @param fileName Name of the asset to retrieve.
+ */
+ public @NonNull AssetFileDescriptor openNonAssetFd(@NonNull String fileName)
throws IOException {
return openNonAssetFd(0, fileName);
}
-
- public final AssetFileDescriptor openNonAssetFd(int cookie,
- String fileName) throws IOException {
+
+ /**
+ * Open a non-asset as an asset by mmapping it and returning an {@link AssetFileDescriptor}.
+ * This provides direct access to all of the files included in an application
+ * package (not only its assets). Applications should not normally use this.
+ *
+ * The asset must not be compressed, or an exception will be thrown.
+ *
+ * @param cookie Identifier of the package to be opened.
+ * @param fileName Name of the asset to retrieve.
+ */
+ public @NonNull AssetFileDescriptor openNonAssetFd(int cookie, @NonNull String fileName)
+ throws IOException {
+ Preconditions.checkNotNull(fileName, "fileName");
synchronized (this) {
- if (!mOpen) {
- throw new RuntimeException("Assetmanager has been closed");
- }
- ParcelFileDescriptor pfd = openNonAssetFdNative(cookie,
- fileName, mOffsets);
- if (pfd != null) {
- return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
+ ensureOpenLocked();
+ final ParcelFileDescriptor pfd =
+ nativeOpenNonAssetFd(mObject, cookie, fileName, mOffsets);
+ if (pfd == null) {
+ throw new FileNotFoundException("Asset absolute file: " + fileName);
}
+ return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
}
- throw new FileNotFoundException("Asset absolute file: " + fileName);
}
/**
@@ -498,7 +907,7 @@ public final class AssetManager implements AutoCloseable {
*
* @param fileName The name of the file to retrieve.
*/
- public final XmlResourceParser openXmlResourceParser(String fileName)
+ public @NonNull XmlResourceParser openXmlResourceParser(@NonNull String fileName)
throws IOException {
return openXmlResourceParser(0, fileName);
}
@@ -509,242 +918,265 @@ public final class AssetManager implements AutoCloseable {
* @param cookie Identifier of the package to be opened.
* @param fileName The name of the file to retrieve.
*/
- public final XmlResourceParser openXmlResourceParser(int cookie,
- String fileName) throws IOException {
- XmlBlock block = openXmlBlockAsset(cookie, fileName);
- XmlResourceParser rp = block.newParser();
- block.close();
- return rp;
+ public @NonNull XmlResourceParser openXmlResourceParser(int cookie, @NonNull String fileName)
+ throws IOException {
+ try (XmlBlock block = openXmlBlockAsset(cookie, fileName)) {
+ XmlResourceParser parser = block.newParser();
+ // If openXmlBlockAsset doesn't throw, it will always return an XmlBlock object with
+ // a valid native pointer, which makes newParser always return non-null. But let's
+ // be paranoid.
+ if (parser == null) {
+ throw new AssertionError("block.newParser() returned a null parser");
+ }
+ return parser;
+ }
}
/**
- * {@hide}
- * Retrieve a non-asset as a compiled XML file. Not for use by
- * applications.
+ * Retrieve a non-asset as a compiled XML file. Not for use by applications.
*
* @param fileName The name of the file to retrieve.
+ * @hide
*/
- /*package*/ final XmlBlock openXmlBlockAsset(String fileName)
- throws IOException {
+ @NonNull XmlBlock openXmlBlockAsset(@NonNull String fileName) throws IOException {
return openXmlBlockAsset(0, fileName);
}
/**
- * {@hide}
* Retrieve a non-asset as a compiled XML file. Not for use by
* applications.
*
* @param cookie Identifier of the package to be opened.
* @param fileName Name of the asset to retrieve.
+ * @hide
*/
- /*package*/ final XmlBlock openXmlBlockAsset(int cookie, String fileName)
- throws IOException {
+ @NonNull XmlBlock openXmlBlockAsset(int cookie, @NonNull String fileName) throws IOException {
+ Preconditions.checkNotNull(fileName, "fileName");
synchronized (this) {
- if (!mOpen) {
- throw new RuntimeException("Assetmanager has been closed");
- }
- long xmlBlock = openXmlAssetNative(cookie, fileName);
- if (xmlBlock != 0) {
- XmlBlock res = new XmlBlock(this, xmlBlock);
- incRefsLocked(res.hashCode());
- return res;
+ ensureOpenLocked();
+ final long xmlBlock = nativeOpenXmlAsset(mObject, cookie, fileName);
+ if (xmlBlock == 0) {
+ throw new FileNotFoundException("Asset XML file: " + fileName);
}
+ final XmlBlock block = new XmlBlock(this, xmlBlock);
+ incRefsLocked(block.hashCode());
+ return block;
}
- throw new FileNotFoundException("Asset XML file: " + fileName);
}
- /*package*/ void xmlBlockGone(int id) {
+ void xmlBlockGone(int id) {
synchronized (this) {
decRefsLocked(id);
}
}
- /*package*/ final long createTheme() {
+ void applyStyle(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes,
+ @Nullable XmlBlock.Parser parser, @NonNull int[] inAttrs, long outValuesAddress,
+ long outIndicesAddress) {
+ Preconditions.checkNotNull(inAttrs, "inAttrs");
synchronized (this) {
- if (!mOpen) {
- throw new RuntimeException("Assetmanager has been closed");
- }
- long res = newTheme();
- incRefsLocked(res);
- return res;
+ // Need to synchronize on AssetManager because we will be accessing
+ // the native implementation of AssetManager.
+ ensureValidLocked();
+ nativeApplyStyle(mObject, themePtr, defStyleAttr, defStyleRes,
+ parser != null ? parser.mParseState : 0, inAttrs, outValuesAddress,
+ outIndicesAddress);
+ }
+ }
+
+ boolean resolveAttrs(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes,
+ @Nullable int[] inValues, @NonNull int[] inAttrs, @NonNull int[] outValues,
+ @NonNull int[] outIndices) {
+ Preconditions.checkNotNull(inAttrs, "inAttrs");
+ Preconditions.checkNotNull(outValues, "outValues");
+ Preconditions.checkNotNull(outIndices, "outIndices");
+ synchronized (this) {
+ // Need to synchronize on AssetManager because we will be accessing
+ // the native implementation of AssetManager.
+ ensureValidLocked();
+ return nativeResolveAttrs(mObject,
+ themePtr, defStyleAttr, defStyleRes, inValues, inAttrs, outValues, outIndices);
}
}
- /*package*/ final void releaseTheme(long theme) {
+ boolean retrieveAttributes(@NonNull XmlBlock.Parser parser, @NonNull int[] inAttrs,
+ @NonNull int[] outValues, @NonNull int[] outIndices) {
+ Preconditions.checkNotNull(parser, "parser");
+ Preconditions.checkNotNull(inAttrs, "inAttrs");
+ Preconditions.checkNotNull(outValues, "outValues");
+ Preconditions.checkNotNull(outIndices, "outIndices");
synchronized (this) {
- deleteTheme(theme);
- decRefsLocked(theme);
+ // Need to synchronize on AssetManager because we will be accessing
+ // the native implementation of AssetManager.
+ ensureValidLocked();
+ return nativeRetrieveAttributes(
+ mObject, parser.mParseState, inAttrs, outValues, outIndices);
}
}
+ long createTheme() {
+ synchronized (this) {
+ ensureValidLocked();
+ long themePtr = nativeThemeCreate(mObject);
+ incRefsLocked(themePtr);
+ return themePtr;
+ }
+ }
+
+ void releaseTheme(long themePtr) {
+ synchronized (this) {
+ nativeThemeDestroy(themePtr);
+ decRefsLocked(themePtr);
+ }
+ }
+
+ void applyStyleToTheme(long themePtr, @StyleRes int resId, boolean force) {
+ synchronized (this) {
+ // Need to synchronize on AssetManager because we will be accessing
+ // the native implementation of AssetManager.
+ ensureValidLocked();
+ nativeThemeApplyStyle(mObject, themePtr, resId, force);
+ }
+ }
+
+ @Override
protected void finalize() throws Throwable {
- try {
- if (DEBUG_REFS && mNumRefs != 0) {
- Log.w(TAG, "AssetManager " + this
- + " finalized with non-zero refs: " + mNumRefs);
- if (mRefStacks != null) {
- for (RuntimeException e : mRefStacks.values()) {
- Log.w(TAG, "Reference from here", e);
- }
+ if (DEBUG_REFS && mNumRefs != 0) {
+ Log.w(TAG, "AssetManager " + this + " finalized with non-zero refs: " + mNumRefs);
+ if (mRefStacks != null) {
+ for (RuntimeException e : mRefStacks.values()) {
+ Log.w(TAG, "Reference from here", e);
}
}
- destroy();
- } finally {
- super.finalize();
+ }
+
+ if (mObject != 0) {
+ nativeDestroy(mObject);
}
}
-
+
+ /* No Locking is needed for AssetInputStream because an AssetInputStream is not-thread
+ safe and it does not rely on AssetManager once it has been created. It completely owns the
+ underlying Asset. */
public final class AssetInputStream extends InputStream {
+ private long mAssetNativePtr;
+ private long mLength;
+ private long mMarkPos;
+
/**
* @hide
*/
public final int getAssetInt() {
throw new UnsupportedOperationException();
}
+
/**
* @hide
*/
public final long getNativeAsset() {
- return mAsset;
+ return mAssetNativePtr;
}
- private AssetInputStream(long asset)
- {
- mAsset = asset;
- mLength = getAssetLength(asset);
+
+ private AssetInputStream(long assetNativePtr) {
+ mAssetNativePtr = assetNativePtr;
+ mLength = nativeAssetGetLength(assetNativePtr);
}
+
+ @Override
public final int read() throws IOException {
- return readAssetChar(mAsset);
- }
- public final boolean markSupported() {
- return true;
- }
- public final int available() throws IOException {
- long len = getAssetRemainingLength(mAsset);
- return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)len;
+ ensureOpen();
+ return nativeAssetReadChar(mAssetNativePtr);
}
- public final void close() throws IOException {
- synchronized (AssetManager.this) {
- if (mAsset != 0) {
- destroyAsset(mAsset);
- mAsset = 0;
- decRefsLocked(hashCode());
- }
- }
- }
- public final void mark(int readlimit) {
- mMarkPos = seekAsset(mAsset, 0, 0);
- }
- public final void reset() throws IOException {
- seekAsset(mAsset, mMarkPos, -1);
- }
- public final int read(byte[] b) throws IOException {
- return readAsset(mAsset, b, 0, b.length);
+
+ @Override
+ public final int read(@NonNull byte[] b) throws IOException {
+ ensureOpen();
+ Preconditions.checkNotNull(b, "b");
+ return nativeAssetRead(mAssetNativePtr, b, 0, b.length);
}
- public final int read(byte[] b, int off, int len) throws IOException {
- return readAsset(mAsset, b, off, len);
+
+ @Override
+ public final int read(@NonNull byte[] b, int off, int len) throws IOException {
+ ensureOpen();
+ Preconditions.checkNotNull(b, "b");
+ return nativeAssetRead(mAssetNativePtr, b, off, len);
}
+
+ @Override
public final long skip(long n) throws IOException {
- long pos = seekAsset(mAsset, 0, 0);
- if ((pos+n) > mLength) {
- n = mLength-pos;
+ ensureOpen();
+ long pos = nativeAssetSeek(mAssetNativePtr, 0, 0);
+ if ((pos + n) > mLength) {
+ n = mLength - pos;
}
if (n > 0) {
- seekAsset(mAsset, n, 0);
+ nativeAssetSeek(mAssetNativePtr, n, 0);
}
return n;
}
- protected void finalize() throws Throwable
- {
- close();
+ @Override
+ public final int available() throws IOException {
+ ensureOpen();
+ final long len = nativeAssetGetRemainingLength(mAssetNativePtr);
+ return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) len;
}
- private long mAsset;
- private long mLength;
- private long mMarkPos;
- }
-
- /**
- * Add an additional set of assets to the asset manager. This can be
- * either a directory or ZIP file. Not for use by applications. Returns
- * the cookie of the added asset, or 0 on failure.
- * {@hide}
- */
- public final int addAssetPath(String path) {
- return addAssetPathInternal(path, false);
- }
-
- /**
- * Add an application assets to the asset manager and loading it as shared library.
- * This can be either a directory or ZIP file. Not for use by applications. Returns
- * the cookie of the added asset, or 0 on failure.
- * {@hide}
- */
- public final int addAssetPathAsSharedLibrary(String path) {
- return addAssetPathInternal(path, true);
- }
-
- private final int addAssetPathInternal(String path, boolean appAsLib) {
- synchronized (this) {
- int res = addAssetPathNative(path, appAsLib);
- makeStringBlocks(mStringBlocks);
- return res;
+ @Override
+ public final boolean markSupported() {
+ return true;
}
- }
-
- private native final int addAssetPathNative(String path, boolean appAsLib);
- /**
- * Add a set of assets to overlay an already added set of assets.
- *
- * This is only intended for application resources. System wide resources
- * are handled before any Java code is executed.
- *
- * {@hide}
- */
+ @Override
+ public final void mark(int readlimit) {
+ ensureOpen();
+ mMarkPos = nativeAssetSeek(mAssetNativePtr, 0, 0);
+ }
- public final int addOverlayPath(String idmapPath) {
- synchronized (this) {
- int res = addOverlayPathNative(idmapPath);
- makeStringBlocks(mStringBlocks);
- return res;
+ @Override
+ public final void reset() throws IOException {
+ ensureOpen();
+ nativeAssetSeek(mAssetNativePtr, mMarkPos, -1);
}
- }
- /**
- * See addOverlayPath.
- *
- * {@hide}
- */
- public native final int addOverlayPathNative(String idmapPath);
+ @Override
+ public final void close() throws IOException {
+ if (mAssetNativePtr != 0) {
+ nativeAssetDestroy(mAssetNativePtr);
+ mAssetNativePtr = 0;
- /**
- * Add multiple sets of assets to the asset manager at once. See
- * {@link #addAssetPath(String)} for more information. Returns array of
- * cookies for each added asset with 0 indicating failure, or null if
- * the input array of paths is null.
- * {@hide}
- */
- public final int[] addAssetPaths(String[] paths) {
- if (paths == null) {
- return null;
+ synchronized (AssetManager.this) {
+ decRefsLocked(hashCode());
+ }
+ }
}
- int[] cookies = new int[paths.length];
- for (int i = 0; i < paths.length; i++) {
- cookies[i] = addAssetPath(paths[i]);
+ @Override
+ protected void finalize() throws Throwable {
+ close();
}
- return cookies;
+ private void ensureOpen() {
+ if (mAssetNativePtr == 0) {
+ throw new IllegalStateException("AssetInputStream is closed");
+ }
+ }
}
/**
* Determine whether the state in this asset manager is up-to-date with
* the files on the filesystem. If false is returned, you need to
* instantiate a new AssetManager class to see the new data.
- * {@hide}
+ * @hide
*/
- public native final boolean isUpToDate();
+ public boolean isUpToDate() {
+ for (ApkAssets apkAssets : getApkAssets()) {
+ if (!apkAssets.isUpToDate()) {
+ return false;
+ }
+ }
+ return true;
+ }
/**
* Get the locales that this asset manager contains data for.
@@ -757,7 +1189,12 @@ public final class AssetManager implements AutoCloseable {
* are of the form {@code ll_CC} where {@code ll} is a two letter language code,
* and {@code CC} is a two letter country code.
*/
- public native final String[] getLocales();
+ public String[] getLocales() {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetLocales(mObject, false /*excludeSystem*/);
+ }
+ }
/**
* Same as getLocales(), except that locales that are only provided by the system (i.e. those
@@ -767,131 +1204,58 @@ public final class AssetManager implements AutoCloseable {
* assets support Cherokee and French, getLocales() would return
* [Cherokee, English, French, German], while getNonSystemLocales() would return
* [Cherokee, French].
- * {@hide}
+ * @hide
*/
- public native final String[] getNonSystemLocales();
-
- /** {@hide} */
- public native final Configuration[] getSizeConfigurations();
+ public String[] getNonSystemLocales() {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetLocales(mObject, true /*excludeSystem*/);
+ }
+ }
/**
- * Change the configuation used when retrieving resources. Not for use by
- * applications.
- * {@hide}
+ * @hide
*/
- public native final void setConfiguration(int mcc, int mnc, String locale,
- int orientation, int touchscreen, int density, int keyboard,
- int keyboardHidden, int navigation, int screenWidth, int screenHeight,
- int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp,
- int screenLayout, int uiMode, int colorMode, int majorVersion);
+ Configuration[] getSizeConfigurations() {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetSizeConfigurations(mObject);
+ }
+ }
/**
- * Retrieve the resource identifier for the given resource name.
+ * Change the configuration used when retrieving resources. Not for use by
+ * applications.
+ * @hide
*/
- /*package*/ native final int getResourceIdentifier(String name,
- String defType,
- String defPackage);
+ public void setConfiguration(int mcc, int mnc, @Nullable String locale, int orientation,
+ int touchscreen, int density, int keyboard, int keyboardHidden, int navigation,
+ int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp,
+ int screenHeightDp, int screenLayout, int uiMode, int colorMode, int majorVersion) {
+ synchronized (this) {
+ ensureValidLocked();
+ nativeSetConfiguration(mObject, mcc, mnc, locale, orientation, touchscreen, density,
+ keyboard, keyboardHidden, navigation, screenWidth, screenHeight,
+ smallestScreenWidthDp, screenWidthDp, screenHeightDp, screenLayout, uiMode,
+ colorMode, majorVersion);
+ }
+ }
- /*package*/ native final String getResourceName(int resid);
- /*package*/ native final String getResourcePackageName(int resid);
- /*package*/ native final String getResourceTypeName(int resid);
- /*package*/ native final String getResourceEntryName(int resid);
-
- private native final long openAsset(String fileName, int accessMode);
- private final native ParcelFileDescriptor openAssetFd(String fileName,
- long[] outOffsets) throws IOException;
- private native final long openNonAssetNative(int cookie, String fileName,
- int accessMode);
- private native ParcelFileDescriptor openNonAssetFdNative(int cookie,
- String fileName, long[] outOffsets) throws IOException;
- private native final void destroyAsset(long asset);
- private native final int readAssetChar(long asset);
- private native final int readAsset(long asset, byte[] b, int off, int len);
- private native final long seekAsset(long asset, long offset, int whence);
- private native final long getAssetLength(long asset);
- private native final long getAssetRemainingLength(long asset);
-
- /** Returns true if the resource was found, filling in mRetStringBlock and
- * mRetData. */
- private native final int loadResourceValue(int ident, short density, TypedValue outValue,
- boolean resolve);
- /** Returns true if the resource was found, filling in mRetStringBlock and
- * mRetData. */
- private native final int loadResourceBagValue(int ident, int bagEntryId, TypedValue outValue,
- boolean resolve);
- /*package*/ static final int STYLE_NUM_ENTRIES = 6;
- /*package*/ static final int STYLE_TYPE = 0;
- /*package*/ static final int STYLE_DATA = 1;
- /*package*/ static final int STYLE_ASSET_COOKIE = 2;
- /*package*/ static final int STYLE_RESOURCE_ID = 3;
-
- /* Offset within typed data array for native changingConfigurations. */
- static final int STYLE_CHANGING_CONFIGURATIONS = 4;
-
- /*package*/ static final int STYLE_DENSITY = 5;
- /*package*/ native static final void applyStyle(long theme,
- int defStyleAttr, int defStyleRes, long xmlParser,
- int[] inAttrs, int length, long outValuesAddress, long outIndicesAddress);
- /*package*/ native static final boolean resolveAttrs(long theme,
- int defStyleAttr, int defStyleRes, int[] inValues,
- int[] inAttrs, int[] outValues, int[] outIndices);
- /*package*/ native final boolean retrieveAttributes(
- long xmlParser, int[] inAttrs, int[] outValues, int[] outIndices);
- /*package*/ native final int getArraySize(int resource);
- /*package*/ native final int retrieveArray(int resource, int[] outValues);
- private native final int getStringBlockCount();
- private native final long getNativeStringBlock(int block);
-
- /**
- * {@hide}
- */
- public native final String getCookieName(int cookie);
-
- /**
- * {@hide}
- */
- public native final SparseArray<String> getAssignedPackageIdentifiers();
-
- /**
- * {@hide}
- */
- public native static final int getGlobalAssetCount();
-
/**
- * {@hide}
+ * @hide
*/
- public native static final String getAssetAllocations();
-
- /**
- * {@hide}
- */
- public native static final int getGlobalAssetManagerCount();
-
- private native final long newTheme();
- private native final void deleteTheme(long theme);
- /*package*/ native static final void applyThemeStyle(long theme, int styleRes, boolean force);
- /*package*/ native static final void copyTheme(long dest, long source);
- /*package*/ native static final void clearTheme(long theme);
- /*package*/ native static final int loadThemeAttributeValue(long theme, int ident,
- TypedValue outValue,
- boolean resolve);
- /*package*/ native static final void dumpTheme(long theme, int priority, String tag, String prefix);
- /*package*/ native static final @NativeConfig int getThemeChangingConfigurations(long theme);
-
- private native final long openXmlAssetNative(int cookie, String fileName);
-
- private native final String[] getArrayStringResource(int arrayRes);
- private native final int[] getArrayStringInfo(int arrayRes);
- /*package*/ native final int[] getArrayIntResource(int arrayRes);
- /*package*/ native final int[] getStyleAttributes(int themeRes);
-
- private native final void init(boolean isSystem);
- private native final void destroy();
-
- private final void incRefsLocked(long id) {
+ public SparseArray<String> getAssignedPackageIdentifiers() {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetAssignedPackageIdentifiers(mObject);
+ }
+ }
+
+ @GuardedBy("this")
+ private void incRefsLocked(long id) {
if (DEBUG_REFS) {
if (mRefStacks == null) {
- mRefStacks = new HashMap<Long, RuntimeException>();
+ mRefStacks = new HashMap<>();
}
RuntimeException ex = new RuntimeException();
ex.fillInStackTrace();
@@ -899,16 +1263,119 @@ public final class AssetManager implements AutoCloseable {
}
mNumRefs++;
}
-
- private final void decRefsLocked(long id) {
+
+ @GuardedBy("this")
+ private void decRefsLocked(long id) {
if (DEBUG_REFS && mRefStacks != null) {
mRefStacks.remove(id);
}
mNumRefs--;
- //System.out.println("Dec streams: mNumRefs=" + mNumRefs
- // + " mReleased=" + mReleased);
- if (mNumRefs == 0) {
- destroy();
+ if (mNumRefs == 0 && mObject != 0) {
+ nativeDestroy(mObject);
+ mObject = 0;
+ mApkAssets = sEmptyApkAssets;
}
}
+
+ // AssetManager setup native methods.
+ private static native long nativeCreate();
+ private static native void nativeDestroy(long ptr);
+ private static native void nativeSetApkAssets(long ptr, @NonNull ApkAssets[] apkAssets,
+ boolean invalidateCaches);
+ private static native void nativeSetConfiguration(long ptr, int mcc, int mnc,
+ @Nullable String locale, int orientation, int touchscreen, int density, int keyboard,
+ int keyboardHidden, int navigation, int screenWidth, int screenHeight,
+ int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout,
+ int uiMode, int colorMode, int majorVersion);
+ private static native @NonNull SparseArray<String> nativeGetAssignedPackageIdentifiers(
+ long ptr);
+
+ // File native methods.
+ private static native @Nullable String[] nativeList(long ptr, @NonNull String path)
+ throws IOException;
+ private static native long nativeOpenAsset(long ptr, @NonNull String fileName, int accessMode);
+ private static native @Nullable ParcelFileDescriptor nativeOpenAssetFd(long ptr,
+ @NonNull String fileName, long[] outOffsets) throws IOException;
+ private static native long nativeOpenNonAsset(long ptr, int cookie, @NonNull String fileName,
+ int accessMode);
+ private static native @Nullable ParcelFileDescriptor nativeOpenNonAssetFd(long ptr, int cookie,
+ @NonNull String fileName, @NonNull long[] outOffsets) throws IOException;
+ private static native long nativeOpenXmlAsset(long ptr, int cookie, @NonNull String fileName);
+
+ // Primitive resource native methods.
+ private static native int nativeGetResourceValue(long ptr, @AnyRes int resId, short density,
+ @NonNull TypedValue outValue, boolean resolveReferences);
+ private static native int nativeGetResourceBagValue(long ptr, @AnyRes int resId, int bagEntryId,
+ @NonNull TypedValue outValue);
+
+ private static native @Nullable @AttrRes int[] nativeGetStyleAttributes(long ptr,
+ @StyleRes int resId);
+ private static native @Nullable String[] nativeGetResourceStringArray(long ptr,
+ @ArrayRes int resId);
+ private static native @Nullable int[] nativeGetResourceStringArrayInfo(long ptr,
+ @ArrayRes int resId);
+ private static native @Nullable int[] nativeGetResourceIntArray(long ptr, @ArrayRes int resId);
+ private static native int nativeGetResourceArraySize(long ptr, @ArrayRes int resId);
+ private static native int nativeGetResourceArray(long ptr, @ArrayRes int resId,
+ @NonNull int[] outValues);
+
+ // Resource name/ID native methods.
+ private static native @AnyRes int nativeGetResourceIdentifier(long ptr, @NonNull String name,
+ @Nullable String defType, @Nullable String defPackage);
+ private static native @Nullable String nativeGetResourceName(long ptr, @AnyRes int resid);
+ private static native @Nullable String nativeGetResourcePackageName(long ptr,
+ @AnyRes int resid);
+ private static native @Nullable String nativeGetResourceTypeName(long ptr, @AnyRes int resid);
+ private static native @Nullable String nativeGetResourceEntryName(long ptr, @AnyRes int resid);
+ private static native @Nullable String[] nativeGetLocales(long ptr, boolean excludeSystem);
+ private static native @Nullable Configuration[] nativeGetSizeConfigurations(long ptr);
+
+ // Style attribute retrieval native methods.
+ private static native void nativeApplyStyle(long ptr, long themePtr, @AttrRes int defStyleAttr,
+ @StyleRes int defStyleRes, long xmlParserPtr, @NonNull int[] inAttrs,
+ long outValuesAddress, long outIndicesAddress);
+ private static native boolean nativeResolveAttrs(long ptr, long themePtr,
+ @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @Nullable int[] inValues,
+ @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices);
+ private static native boolean nativeRetrieveAttributes(long ptr, long xmlParserPtr,
+ @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices);
+
+ // Theme related native methods
+ private static native long nativeThemeCreate(long ptr);
+ private static native void nativeThemeDestroy(long themePtr);
+ private static native void nativeThemeApplyStyle(long ptr, long themePtr, @StyleRes int resId,
+ boolean force);
+ static native void nativeThemeCopy(long destThemePtr, long sourceThemePtr);
+ static native void nativeThemeClear(long themePtr);
+ private static native int nativeThemeGetAttributeValue(long ptr, long themePtr,
+ @AttrRes int resId, @NonNull TypedValue outValue, boolean resolve);
+ private static native void nativeThemeDump(long ptr, long themePtr, int priority, String tag,
+ String prefix);
+ static native @NativeConfig int nativeThemeGetChangingConfigurations(long themePtr);
+
+ // AssetInputStream related native methods.
+ private static native void nativeAssetDestroy(long assetPtr);
+ private static native int nativeAssetReadChar(long assetPtr);
+ private static native int nativeAssetRead(long assetPtr, byte[] b, int off, int len);
+ private static native long nativeAssetSeek(long assetPtr, long offset, int whence);
+ private static native long nativeAssetGetLength(long assetPtr);
+ private static native long nativeAssetGetRemainingLength(long assetPtr);
+
+ private static native void nativeVerifySystemIdmaps();
+
+ // Global debug native methods.
+ /**
+ * @hide
+ */
+ public static native int getGlobalAssetCount();
+
+ /**
+ * @hide
+ */
+ public static native String getAssetAllocations();
+
+ /**
+ * @hide
+ */
+ public static native int getGlobalAssetManagerCount();
}
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index f7cccd56f079..193e56ef404d 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -16,18 +16,44 @@
package android.content.res;
+import static android.content.ConfigurationProto.COLOR_MODE;
+import static android.content.ConfigurationProto.DENSITY_DPI;
+import static android.content.ConfigurationProto.FONT_SCALE;
+import static android.content.ConfigurationProto.HARD_KEYBOARD_HIDDEN;
+import static android.content.ConfigurationProto.KEYBOARD;
+import static android.content.ConfigurationProto.KEYBOARD_HIDDEN;
+import static android.content.ConfigurationProto.LOCALES;
+import static android.content.ConfigurationProto.MCC;
+import static android.content.ConfigurationProto.MNC;
+import static android.content.ConfigurationProto.NAVIGATION;
+import static android.content.ConfigurationProto.NAVIGATION_HIDDEN;
+import static android.content.ConfigurationProto.ORIENTATION;
+import static android.content.ConfigurationProto.SCREEN_HEIGHT_DP;
+import static android.content.ConfigurationProto.SCREEN_LAYOUT;
+import static android.content.ConfigurationProto.SCREEN_WIDTH_DP;
+import static android.content.ConfigurationProto.SMALLEST_SCREEN_WIDTH_DP;
+import static android.content.ConfigurationProto.TOUCHSCREEN;
+import static android.content.ConfigurationProto.UI_MODE;
+import static android.content.ConfigurationProto.WINDOW_CONFIGURATION;
+import static android.content.ResourcesConfigurationProto.CONFIGURATION;
+import static android.content.ResourcesConfigurationProto.SCREEN_HEIGHT_PX;
+import static android.content.ResourcesConfigurationProto.SCREEN_WIDTH_PX;
+import static android.content.ResourcesConfigurationProto.SDK_VERSION;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.app.WindowConfiguration;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo.Config;
-import android.graphics.Rect;
import android.os.Build;
import android.os.LocaleList;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
-import android.view.DisplayInfo;
+import android.util.DisplayMetrics;
+import android.util.proto.ProtoOutputStream;
import android.view.View;
import com.android.internal.util.XmlUtils;
@@ -42,7 +68,6 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Locale;
-
/**
* This class describes all device configuration information that can
* impact the resources the application retrieves. This includes both
@@ -297,14 +322,13 @@ public final class Configuration implements Parcelable, Comparable<Configuration
public int screenLayout;
/**
+ * Configuration relating to the windowing state of the object associated with this
+ * Configuration. Contents of this field are not intended to affect resources, but need to be
+ * communicated and propagated at the same time as the rest of Configuration.
* @hide
- * {@link android.graphics.Rect} defining app bounds. The dimensions override usages of
- * {@link DisplayInfo#appHeight} and {@link DisplayInfo#appWidth} and mirrors these values at
- * the display level. Lower levels can override these values to provide custom bounds to enforce
- * features such as a max aspect ratio.
- * TODO(b/36812336): Move appBounds out of {@link Configuration}.
*/
- public Rect appBounds;
+ @TestApi
+ public final WindowConfiguration windowConfiguration = new WindowConfiguration();
/** @hide */
static public int resetScreenLayout(int curLayout) {
@@ -772,25 +796,24 @@ public final class Configuration implements Parcelable, Comparable<Configuration
public int seq;
/** @hide */
- @IntDef(flag = true,
- value = {
- NATIVE_CONFIG_MCC,
- NATIVE_CONFIG_MNC,
- NATIVE_CONFIG_LOCALE,
- NATIVE_CONFIG_TOUCHSCREEN,
- NATIVE_CONFIG_KEYBOARD,
- NATIVE_CONFIG_KEYBOARD_HIDDEN,
- NATIVE_CONFIG_NAVIGATION,
- NATIVE_CONFIG_ORIENTATION,
- NATIVE_CONFIG_DENSITY,
- NATIVE_CONFIG_SCREEN_SIZE,
- NATIVE_CONFIG_VERSION,
- NATIVE_CONFIG_SCREEN_LAYOUT,
- NATIVE_CONFIG_UI_MODE,
- NATIVE_CONFIG_SMALLEST_SCREEN_SIZE,
- NATIVE_CONFIG_LAYOUTDIR,
- NATIVE_CONFIG_COLOR_MODE,
- })
+ @IntDef(flag = true, prefix = { "NATIVE_CONFIG_" }, value = {
+ NATIVE_CONFIG_MCC,
+ NATIVE_CONFIG_MNC,
+ NATIVE_CONFIG_LOCALE,
+ NATIVE_CONFIG_TOUCHSCREEN,
+ NATIVE_CONFIG_KEYBOARD,
+ NATIVE_CONFIG_KEYBOARD_HIDDEN,
+ NATIVE_CONFIG_NAVIGATION,
+ NATIVE_CONFIG_ORIENTATION,
+ NATIVE_CONFIG_DENSITY,
+ NATIVE_CONFIG_SCREEN_SIZE,
+ NATIVE_CONFIG_VERSION,
+ NATIVE_CONFIG_SCREEN_LAYOUT,
+ NATIVE_CONFIG_UI_MODE,
+ NATIVE_CONFIG_SMALLEST_SCREEN_SIZE,
+ NATIVE_CONFIG_LAYOUTDIR,
+ NATIVE_CONFIG_COLOR_MODE,
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface NativeConfig {}
@@ -895,9 +918,9 @@ public final class Configuration implements Parcelable, Comparable<Configuration
compatScreenWidthDp = o.compatScreenWidthDp;
compatScreenHeightDp = o.compatScreenHeightDp;
compatSmallestScreenWidthDp = o.compatSmallestScreenWidthDp;
- setAppBounds(o.appBounds);
assetsSeq = o.assetsSeq;
seq = o.seq;
+ windowConfiguration.setTo(o.windowConfiguration);
}
public String toString() {
@@ -1046,9 +1069,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
case NAVIGATIONHIDDEN_YES: sb.append("/h"); break;
default: sb.append("/"); sb.append(navigationHidden); break;
}
- if (appBounds != null) {
- sb.append(" appBounds="); sb.append(appBounds);
- }
+ sb.append(" winConfig="); sb.append(windowConfiguration);
if (assetsSeq != 0) {
sb.append(" as.").append(assetsSeq);
}
@@ -1060,6 +1081,95 @@ public final class Configuration implements Parcelable, Comparable<Configuration
}
/**
+ * Write to a protocol buffer output stream.
+ * Protocol buffer message definition at {@link android.content.ConfigurationProto}
+ *
+ * @param protoOutputStream Stream to write the Configuration object to.
+ * @param fieldId Field Id of the Configuration as defined in the parent message
+ * @hide
+ */
+ public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
+ final long token = protoOutputStream.start(fieldId);
+ protoOutputStream.write(FONT_SCALE, fontScale);
+ protoOutputStream.write(MCC, mcc);
+ protoOutputStream.write(MNC, mnc);
+ mLocaleList.writeToProto(protoOutputStream, LOCALES);
+ protoOutputStream.write(SCREEN_LAYOUT, screenLayout);
+ protoOutputStream.write(COLOR_MODE, colorMode);
+ protoOutputStream.write(TOUCHSCREEN, touchscreen);
+ protoOutputStream.write(KEYBOARD, keyboard);
+ protoOutputStream.write(KEYBOARD_HIDDEN, keyboardHidden);
+ protoOutputStream.write(HARD_KEYBOARD_HIDDEN, hardKeyboardHidden);
+ protoOutputStream.write(NAVIGATION, navigation);
+ protoOutputStream.write(NAVIGATION_HIDDEN, navigationHidden);
+ protoOutputStream.write(ORIENTATION, orientation);
+ protoOutputStream.write(UI_MODE, uiMode);
+ protoOutputStream.write(SCREEN_WIDTH_DP, screenWidthDp);
+ protoOutputStream.write(SCREEN_HEIGHT_DP, screenHeightDp);
+ protoOutputStream.write(SMALLEST_SCREEN_WIDTH_DP, smallestScreenWidthDp);
+ protoOutputStream.write(DENSITY_DPI, densityDpi);
+ windowConfiguration.writeToProto(protoOutputStream, WINDOW_CONFIGURATION);
+ protoOutputStream.end(token);
+ }
+
+ /**
+ * Write full {@link android.content.ResourcesConfigurationProto} to protocol buffer output
+ * stream.
+ *
+ * @param protoOutputStream Stream to write the Configuration object to.
+ * @param fieldId Field Id of the Configuration as defined in the parent message
+ * @param metrics Current display information
+ * @hide
+ */
+ public void writeResConfigToProto(ProtoOutputStream protoOutputStream, long fieldId,
+ DisplayMetrics metrics) {
+ final int width, height;
+ if (metrics.widthPixels >= metrics.heightPixels) {
+ width = metrics.widthPixels;
+ height = metrics.heightPixels;
+ } else {
+ //noinspection SuspiciousNameCombination
+ width = metrics.heightPixels;
+ //noinspection SuspiciousNameCombination
+ height = metrics.widthPixels;
+ }
+
+ final long token = protoOutputStream.start(fieldId);
+ writeToProto(protoOutputStream, CONFIGURATION);
+ protoOutputStream.write(SDK_VERSION, Build.VERSION.RESOURCES_SDK_INT);
+ protoOutputStream.write(SCREEN_WIDTH_PX, width);
+ protoOutputStream.write(SCREEN_HEIGHT_PX, height);
+ protoOutputStream.end(token);
+ }
+
+ /**
+ * Convert the UI mode to a human readable format.
+ * @hide
+ */
+ public static String uiModeToString(int uiMode) {
+ switch (uiMode) {
+ case UI_MODE_TYPE_UNDEFINED:
+ return "UI_MODE_TYPE_UNDEFINED";
+ case UI_MODE_TYPE_NORMAL:
+ return "UI_MODE_TYPE_NORMAL";
+ case UI_MODE_TYPE_DESK:
+ return "UI_MODE_TYPE_DESK";
+ case UI_MODE_TYPE_CAR:
+ return "UI_MODE_TYPE_CAR";
+ case UI_MODE_TYPE_TELEVISION:
+ return "UI_MODE_TYPE_TELEVISION";
+ case UI_MODE_TYPE_APPLIANCE:
+ return "UI_MODE_TYPE_APPLIANCE";
+ case UI_MODE_TYPE_WATCH:
+ return "UI_MODE_TYPE_WATCH";
+ case UI_MODE_TYPE_VR_HEADSET:
+ return "UI_MODE_TYPE_VR_HEADSET";
+ default:
+ return Integer.toString(uiMode);
+ }
+ }
+
+ /**
* Set this object to the system defaults.
*/
public void setToDefaults() {
@@ -1083,8 +1193,8 @@ public final class Configuration implements Parcelable, Comparable<Configuration
smallestScreenWidthDp = compatSmallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
densityDpi = DENSITY_DPI_UNDEFINED;
assetsSeq = ASSETS_SEQ_UNDEFINED;
- appBounds = null;
seq = 0;
+ windowConfiguration.setToDefaults();
}
/**
@@ -1183,7 +1293,6 @@ public final class Configuration implements Parcelable, Comparable<Configuration
changed |= ActivityInfo.CONFIG_ORIENTATION;
orientation = delta.orientation;
}
-
if (((delta.screenLayout & SCREENLAYOUT_SIZE_MASK) != SCREENLAYOUT_SIZE_UNDEFINED)
&& (delta.screenLayout & SCREENLAYOUT_SIZE_MASK)
!= (screenLayout & SCREENLAYOUT_SIZE_MASK)) {
@@ -1271,10 +1380,6 @@ public final class Configuration implements Parcelable, Comparable<Configuration
if (delta.compatSmallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
compatSmallestScreenWidthDp = delta.compatSmallestScreenWidthDp;
}
- if (delta.appBounds != null && !delta.appBounds.equals(appBounds)) {
- changed |= ActivityInfo.CONFIG_SCREEN_SIZE;
- setAppBounds(delta.appBounds);
- }
if (delta.assetsSeq != ASSETS_SEQ_UNDEFINED && delta.assetsSeq != assetsSeq) {
changed |= ActivityInfo.CONFIG_ASSETS_PATHS;
assetsSeq = delta.assetsSeq;
@@ -1282,6 +1387,9 @@ public final class Configuration implements Parcelable, Comparable<Configuration
if (delta.seq != 0) {
seq = delta.seq;
}
+ if (windowConfiguration.updateFrom(delta.windowConfiguration) != 0) {
+ changed |= ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
+ }
return changed;
}
@@ -1433,13 +1541,10 @@ public final class Configuration implements Parcelable, Comparable<Configuration
changed |= ActivityInfo.CONFIG_ASSETS_PATHS;
}
- // Make sure that one of the values is not null and that they are not equal.
- if ((compareUndefined || delta.appBounds != null)
- && appBounds != delta.appBounds
- && (appBounds == null || (!publicOnly && !appBounds.equals(delta.appBounds))
- || (publicOnly && (appBounds.width() != delta.appBounds.width()
- || appBounds.height() != delta.appBounds.height())))) {
- changed |= ActivityInfo.CONFIG_SCREEN_SIZE;
+ // WindowConfiguration differences aren't considered public...
+ if (!publicOnly
+ && windowConfiguration.diff(delta.windowConfiguration, compareUndefined) != 0) {
+ changed |= ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
}
return changed;
@@ -1508,12 +1613,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
dest.writeInt(mnc);
fixUpLocaleList();
- final int localeListSize = mLocaleList.size();
- dest.writeInt(localeListSize);
- for (int i = 0; i < localeListSize; ++i) {
- final Locale l = mLocaleList.get(i);
- dest.writeString(l.toLanguageTag());
- }
+ dest.writeParcelable(mLocaleList, flags);
if(userSetLocale) {
dest.writeInt(1);
@@ -1537,7 +1637,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
dest.writeInt(compatScreenWidthDp);
dest.writeInt(compatScreenHeightDp);
dest.writeInt(compatSmallestScreenWidthDp);
- dest.writeValue(appBounds);
+ dest.writeValue(windowConfiguration);
dest.writeInt(assetsSeq);
dest.writeInt(seq);
}
@@ -1547,12 +1647,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
mcc = source.readInt();
mnc = source.readInt();
- final int localeListSize = source.readInt();
- final Locale[] localeArray = new Locale[localeListSize];
- for (int i = 0; i < localeListSize; ++i) {
- localeArray[i] = Locale.forLanguageTag(source.readString());
- }
- mLocaleList = new LocaleList(localeArray);
+ mLocaleList = source.readParcelable(LocaleList.class.getClassLoader());
locale = mLocaleList.get(0);
userSetLocale = (source.readInt()==1);
@@ -1573,7 +1668,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
compatScreenWidthDp = source.readInt();
compatScreenHeightDp = source.readInt();
compatSmallestScreenWidthDp = source.readInt();
- appBounds = (Rect) source.readValue(null);
+ windowConfiguration.setTo((WindowConfiguration) source.readValue(null));
assetsSeq = source.readInt();
seq = source.readInt();
}
@@ -1663,21 +1758,8 @@ public final class Configuration implements Parcelable, Comparable<Configuration
if (n != 0) return n;
n = this.assetsSeq - that.assetsSeq;
if (n != 0) return n;
-
- if (this.appBounds == null && that.appBounds != null) {
- return 1;
- } else if (this.appBounds != null && that.appBounds == null) {
- return -1;
- } else if (this.appBounds != null && that.appBounds != null) {
- n = this.appBounds.left - that.appBounds.left;
- if (n != 0) return n;
- n = this.appBounds.top - that.appBounds.top;
- if (n != 0) return n;
- n = this.appBounds.right - that.appBounds.right;
- if (n != 0) return n;
- n = this.appBounds.bottom - that.appBounds.bottom;
- if (n != 0) return n;
- }
+ n = windowConfiguration.compareTo(that.windowConfiguration);
+ if (n != 0) return n;
// if (n != 0) return n;
return n;
@@ -1768,33 +1850,6 @@ public final class Configuration implements Parcelable, Comparable<Configuration
/**
* @hide
*
- * Helper method for setting the app bounds.
- */
- public void setAppBounds(Rect rect) {
- if (rect == null) {
- appBounds = null;
- return;
- }
-
- setAppBounds(rect.left, rect.top, rect.right, rect.bottom);
- }
-
- /**
- * @hide
- *
- * Helper method for setting the app bounds.
- */
- public void setAppBounds(int left, int top, int right, int bottom) {
- if (appBounds == null) {
- appBounds = new Rect();
- }
-
- appBounds.set(left, top, right, bottom);
- }
-
- /**
- * @hide
- *
* Clears the locale without changing layout direction.
*/
public void clearLocales() {
@@ -1850,6 +1905,10 @@ public final class Configuration implements Parcelable, Comparable<Configuration
* Return whether the screen has a wide color gamut and wide color gamut rendering
* is supported by this device.
*
+ * When true, it implies the screen is colorspace aware but not
+ * necessarily color-managed. The final colors may still be changed by the
+ * screen depending on user settings.
+ *
* @return true if the screen has a wide color gamut and wide color gamut rendering
* is supported, false otherwise
*/
@@ -1915,11 +1974,21 @@ public final class Configuration implements Parcelable, Comparable<Configuration
/**
* Returns a string representation of the configuration that can be parsed
- * by build tools (like AAPT).
+ * by build tools (like AAPT), without display metrics included
*
* @hide
*/
public static String resourceQualifierString(Configuration config) {
+ return resourceQualifierString(config, null);
+ }
+
+ /**
+ * Returns a string representation of the configuration that can be parsed
+ * by build tools (like AAPT).
+ *
+ * @hide
+ */
+ public static String resourceQualifierString(Configuration config, DisplayMetrics metrics) {
ArrayList<String> parts = new ArrayList<String>();
if (config.mcc != 0) {
@@ -2094,6 +2163,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
break;
case DENSITY_DPI_NONE:
parts.add("nodpi");
+ break;
default:
parts.add(config.densityDpi + "dpi");
break;
@@ -2166,6 +2236,20 @@ public final class Configuration implements Parcelable, Comparable<Configuration
break;
}
+ if (metrics != null) {
+ final int width, height;
+ if (metrics.widthPixels >= metrics.heightPixels) {
+ width = metrics.widthPixels;
+ height = metrics.heightPixels;
+ } else {
+ //noinspection SuspiciousNameCombination
+ width = metrics.heightPixels;
+ //noinspection SuspiciousNameCombination
+ height = metrics.widthPixels;
+ }
+ parts.add(width + "x" + height);
+ }
+
parts.add("v" + Build.VERSION.RESOURCES_SDK_INT);
return TextUtils.join("-", parts);
}
@@ -2282,6 +2366,10 @@ public final class Configuration implements Parcelable, Comparable<Configuration
if (base.assetsSeq != change.assetsSeq) {
delta.assetsSeq = change.assetsSeq;
}
+
+ if (!base.windowConfiguration.equals(change.windowConfiguration)) {
+ delta.windowConfiguration.setTo(change.windowConfiguration);
+ }
return delta;
}
@@ -2354,10 +2442,9 @@ public final class Configuration implements Parcelable, Comparable<Configuration
SMALLEST_SCREEN_WIDTH_DP_UNDEFINED);
configOut.densityDpi = XmlUtils.readIntAttribute(parser, XML_ATTR_DENSITY,
DENSITY_DPI_UNDEFINED);
- configOut.appBounds =
- Rect.unflattenFromString(XmlUtils.readStringAttribute(parser, XML_ATTR_APP_BOUNDS));
- // For persistence, we don't care about assetsSeq, so do not read it out.
+ // For persistence, we don't care about assetsSeq and WindowConfiguration, so do not read it
+ // out.
}
@@ -2427,11 +2514,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
XmlUtils.writeIntAttribute(xml, XML_ATTR_DENSITY, config.densityDpi);
}
- if (config.appBounds != null) {
- XmlUtils.writeStringAttribute(xml, XML_ATTR_APP_BOUNDS,
- config.appBounds.flattenToString());
- }
-
- // For persistence, we do not care about assetsSeq, so do not write it out.
+ // For persistence, we do not care about assetsSeq and window configuration, so do not write
+ // it out.
}
}
diff --git a/core/java/android/content/res/FontResourcesParser.java b/core/java/android/content/res/FontResourcesParser.java
index 042eb87f6fb9..6a4aae66c848 100644
--- a/core/java/android/content/res/FontResourcesParser.java
+++ b/core/java/android/content/res/FontResourcesParser.java
@@ -15,7 +15,6 @@
*/
package android.content.res;
-import com.android.internal.R;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Typeface;
@@ -23,6 +22,8 @@ import android.util.AttributeSet;
import android.util.Log;
import android.util.Xml;
+import com.android.internal.R;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -78,12 +79,17 @@ public class FontResourcesParser {
private final @NonNull String mFileName;
private int mWeight;
private int mItalic;
+ private int mTtcIndex;
+ private String mVariationSettings;
private int mResourceId;
- public FontFileResourceEntry(@NonNull String fileName, int weight, int italic) {
+ public FontFileResourceEntry(@NonNull String fileName, int weight, int italic,
+ @Nullable String variationSettings, int ttcIndex) {
mFileName = fileName;
mWeight = weight;
mItalic = italic;
+ mVariationSettings = variationSettings;
+ mTtcIndex = ttcIndex;
}
public @NonNull String getFileName() {
@@ -97,6 +103,14 @@ public class FontResourcesParser {
public int getItalic() {
return mItalic;
}
+
+ public @Nullable String getVariationSettings() {
+ return mVariationSettings;
+ }
+
+ public int getTtcIndex() {
+ return mTtcIndex;
+ }
}
// A class represents file based font-family element in xml file.
@@ -203,6 +217,9 @@ public class FontResourcesParser {
Typeface.RESOLVE_BY_FONT_TABLE);
int italic = array.getInt(R.styleable.FontFamilyFont_fontStyle,
Typeface.RESOLVE_BY_FONT_TABLE);
+ String variationSettings = array.getString(
+ R.styleable.FontFamilyFont_fontVariationSettings);
+ int ttcIndex = array.getInt(R.styleable.FontFamilyFont_ttcIndex, 0);
String filename = array.getString(R.styleable.FontFamilyFont_font);
array.recycle();
while (parser.next() != XmlPullParser.END_TAG) {
@@ -211,7 +228,7 @@ public class FontResourcesParser {
if (filename == null) {
return null;
}
- return new FontFileResourceEntry(filename, weight, italic);
+ return new FontFileResourceEntry(filename, weight, italic, variationSettings, ttcIndex);
}
private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
diff --git a/core/java/android/content/res/GradientColor.java b/core/java/android/content/res/GradientColor.java
index e4659613147d..35ad5033e395 100644
--- a/core/java/android/content/res/GradientColor.java
+++ b/core/java/android/content/res/GradientColor.java
@@ -75,9 +75,14 @@ public class GradientColor extends ComplexColor {
private static final boolean DBG_GRADIENT = false;
- @IntDef({TILE_MODE_CLAMP, TILE_MODE_REPEAT, TILE_MODE_MIRROR})
+ @IntDef(prefix = { "TILE_MODE_" }, value = {
+ TILE_MODE_CLAMP,
+ TILE_MODE_REPEAT,
+ TILE_MODE_MIRROR
+ })
@Retention(RetentionPolicy.SOURCE)
private @interface GradientTileMode {}
+
private static final int TILE_MODE_CLAMP = 0;
private static final int TILE_MODE_REPEAT = 1;
private static final int TILE_MODE_MIRROR = 2;
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index e173653cd961..7adea6a880be 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -128,6 +128,14 @@ public class Resources {
private final ArrayList<WeakReference<Theme>> mThemeRefs = new ArrayList<>();
/**
+ * To avoid leaking WeakReferences to garbage collected Themes on the
+ * mThemeRefs list, we flush the list of stale references any time the
+ * mThemeRefNextFlushSize is reached.
+ */
+ private static final int MIN_THEME_REFS_FLUSH_SIZE = 32;
+ private int mThemeRefsNextFlushSize = MIN_THEME_REFS_FLUSH_SIZE;
+
+ /**
* Returns the most appropriate default theme for the specified target SDK version.
* <ul>
* <li>Below API 11: Gingerbread
@@ -590,7 +598,7 @@ public class Resources {
*/
@NonNull
public int[] getIntArray(@ArrayRes int id) throws NotFoundException {
- int[] res = mResourcesImpl.getAssets().getArrayIntResource(id);
+ int[] res = mResourcesImpl.getAssets().getResourceIntArray(id);
if (res != null) {
return res;
}
@@ -613,13 +621,13 @@ public class Resources {
@NonNull
public TypedArray obtainTypedArray(@ArrayRes int id) throws NotFoundException {
final ResourcesImpl impl = mResourcesImpl;
- int len = impl.getAssets().getArraySize(id);
+ int len = impl.getAssets().getResourceArraySize(id);
if (len < 0) {
throw new NotFoundException("Array resource ID #0x" + Integer.toHexString(id));
}
TypedArray array = TypedArray.obtain(this, len);
- array.mLength = impl.getAssets().retrieveArray(id, array.mData);
+ array.mLength = impl.getAssets().getResourceArray(id, array.mData);
array.mIndices[0] = 0;
return array;
@@ -847,6 +855,7 @@ public class Resources {
* @see #getDrawableForDensity(int, int, Theme)
* @deprecated Use {@link #getDrawableForDensity(int, int, Theme)} instead.
*/
+ @Nullable
@Deprecated
public Drawable getDrawableForDensity(@DrawableRes int id, int density)
throws NotFoundException {
@@ -864,11 +873,13 @@ public class Resources {
* found in {@link DisplayMetrics}. A value of 0 means to use the
* density returned from {@link #getConfiguration()}.
* This is equivalent to calling {@link #getDrawable(int, Theme)}.
- * @param theme The theme used to style the drawable attributes, may be {@code null}.
+ * @param theme The theme used to style the drawable attributes, may be {@code null} if the
+ * drawable cannot be decoded.
* @return Drawable An object that can be used to draw this resource.
* @throws NotFoundException Throws NotFoundException if the given ID does
* not exist.
*/
+ @Nullable
public Drawable getDrawableForDensity(@DrawableRes int id, int density, @Nullable Theme theme) {
final TypedValue value = obtainTempTypedValue();
try {
@@ -980,7 +991,7 @@ public class Resources {
* or multiple colors that can be selected based on a state.
* @deprecated Use {@link #getColorStateList(int, Theme)} instead.
*/
- @Nullable
+ @NonNull
@Deprecated
public ColorStateList getColorStateList(@ColorRes int id) throws NotFoundException {
final ColorStateList csl = getColorStateList(id, null);
@@ -1011,7 +1022,7 @@ public class Resources {
* @return A themed ColorStateList object containing either a single solid
* color or multiple colors that can be selected based on a state.
*/
- @Nullable
+ @NonNull
public ColorStateList getColorStateList(@ColorRes int id, @Nullable Theme theme)
throws NotFoundException {
final TypedValue value = obtainTempTypedValue();
@@ -1024,7 +1035,7 @@ public class Resources {
}
}
- @Nullable
+ @NonNull
ColorStateList loadColorStateList(@NonNull TypedValue value, int id, @Nullable Theme theme)
throws NotFoundException {
return mResourcesImpl.loadColorStateList(this, value, id, theme);
@@ -1033,7 +1044,7 @@ public class Resources {
/**
* @hide
*/
- @Nullable
+ @NonNull
public ComplexColor loadComplexColor(@NonNull TypedValue value, int id, @Nullable Theme theme) {
return mResourcesImpl.loadComplexColor(this, value, id, theme);
}
@@ -1139,6 +1150,7 @@ public class Resources {
*
* @see #getXml
*/
+ @NonNull
public XmlResourceParser getLayout(@LayoutRes int id) throws NotFoundException {
return loadXmlResourceParser(id, "layout");
}
@@ -1163,6 +1175,7 @@ public class Resources {
*
* @see #getXml
*/
+ @NonNull
public XmlResourceParser getAnimation(@AnimatorRes @AnimRes int id) throws NotFoundException {
return loadXmlResourceParser(id, "anim");
}
@@ -1188,6 +1201,7 @@ public class Resources {
*
* @see android.util.AttributeSet
*/
+ @NonNull
public XmlResourceParser getXml(@XmlRes int id) throws NotFoundException {
return loadXmlResourceParser(id, "xml");
}
@@ -1203,8 +1217,8 @@ public class Resources {
* @return InputStream Access to the resource data.
*
* @throws NotFoundException Throws NotFoundException if the given ID does not exist.
- *
*/
+ @NonNull
public InputStream openRawResource(@RawRes int id) throws NotFoundException {
final TypedValue value = obtainTempTypedValue();
try {
@@ -1261,6 +1275,7 @@ public class Resources {
*
* @throws NotFoundException Throws NotFoundException if the given ID does not exist.
*/
+ @NonNull
public InputStream openRawResource(@RawRes int id, TypedValue value)
throws NotFoundException {
return mResourcesImpl.openRawResource(id, value);
@@ -1763,6 +1778,13 @@ public class Resources {
theme.setImpl(mResourcesImpl.newThemeImpl());
synchronized (mThemeRefs) {
mThemeRefs.add(new WeakReference<>(theme));
+
+ // Clean up references to garbage collected themes
+ if (mThemeRefs.size() > mThemeRefsNextFlushSize) {
+ mThemeRefs.removeIf(ref -> ref.get() == null);
+ mThemeRefsNextFlushSize = Math.max(MIN_THEME_REFS_FLUSH_SIZE,
+ 2 * mThemeRefs.size());
+ }
}
return theme;
}
@@ -1789,8 +1811,7 @@ public class Resources {
// out the attributes from the XML file (applying type information
// contained in the resources and such).
XmlBlock.Parser parser = (XmlBlock.Parser)set;
- mResourcesImpl.getAssets().retrieveAttributes(parser.mParseState, attrs,
- array.mData, array.mIndices);
+ mResourcesImpl.getAssets().retrieveAttributes(parser, attrs, array.mData, array.mIndices);
array.mXml = parser;
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 386239cf4f93..8c980677fdd0 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -27,9 +27,11 @@ import android.annotation.StyleRes;
import android.annotation.StyleableRes;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo.Config;
+import android.content.res.AssetManager.AssetInputStream;
import android.content.res.Configuration.NativeConfig;
import android.content.res.Resources.NotFoundException;
import android.graphics.Bitmap;
+import android.graphics.ImageDecoder;
import android.graphics.Typeface;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
@@ -49,6 +51,8 @@ import android.util.TypedValue;
import android.util.Xml;
import android.view.DisplayAdjustments;
+import com.android.internal.util.GrowingArrayUtils;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -117,6 +121,13 @@ public class ResourcesImpl {
private final ConfigurationBoundResourceCache<StateListAnimator> mStateListAnimatorCache =
new ConfigurationBoundResourceCache<>();
+ // A stack of all the resourceIds already referenced when parsing a resource. This is used to
+ // detect circular references in the xml.
+ // Using a ThreadLocal variable ensures that we have different stacks for multiple parallel
+ // calls to ResourcesImpl
+ private final ThreadLocal<LookupStack> mLookupStack =
+ ThreadLocal.withInitial(() -> new LookupStack());
+
/** Size of the cyclical cache used to map XML files to blocks. */
private static final int XML_BLOCK_CACHE_SIZE = 4;
@@ -159,7 +170,6 @@ public class ResourcesImpl {
mDisplayAdjustments = displayAdjustments;
mConfiguration.setToDefaults();
updateConfiguration(config, metrics, displayAdjustments.getCompatibilityInfo());
- mAssets.ensureStringBlocks();
}
public DisplayAdjustments getDisplayAdjustments() {
@@ -618,7 +628,7 @@ public class ResourcesImpl {
} else if (isColorDrawable) {
dr = new ColorDrawable(value.data);
} else {
- dr = loadDrawableForCookie(wrapper, value, id, density, null);
+ dr = loadDrawableForCookie(wrapper, value, id, density);
}
// DrawableContainer' constant state has drawables instances. In order to leave the
// constant state intact in the cache, we need to create a new DrawableContainer after
@@ -743,10 +753,34 @@ public class ResourcesImpl {
}
/**
+ * Loads a Drawable from an encoded image stream, or null.
+ *
+ * This call will handle closing ais.
+ */
+ @Nullable
+ private Drawable decodeImageDrawable(@NonNull AssetInputStream ais,
+ @NonNull Resources wrapper, @NonNull TypedValue value) {
+ ImageDecoder.Source src = new ImageDecoder.AssetInputStreamSource(ais,
+ wrapper, value);
+ try {
+ return ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ });
+ } catch (IOException ioe) {
+ // This is okay. This may be something that ImageDecoder does not
+ // support, like SVG.
+ return null;
+ }
+ }
+
+ /**
* Loads a drawable from XML or resources stream.
+ *
+ * @return Drawable, or null if Drawable cannot be decoded.
*/
+ @Nullable
private Drawable loadDrawableForCookie(@NonNull Resources wrapper, @NonNull TypedValue value,
- int id, int density, @Nullable Resources.Theme theme) {
+ int id, int density) {
if (value.string == null) {
throw new NotFoundException("Resource \"" + getResourceName(id) + "\" ("
+ Integer.toHexString(id) + ") is not a Drawable (color or path): " + value);
@@ -765,36 +799,47 @@ public class ResourcesImpl {
}
}
- // For prelaod tracing.
+ // For preload tracing.
long startTime = 0;
int startBitmapCount = 0;
long startBitmapSize = 0;
- int startDrwableCount = 0;
+ int startDrawableCount = 0;
if (TRACE_FOR_DETAILED_PRELOAD) {
startTime = System.nanoTime();
startBitmapCount = Bitmap.sPreloadTracingNumInstantiatedBitmaps;
startBitmapSize = Bitmap.sPreloadTracingTotalBitmapsSize;
- startDrwableCount = sPreloadTracingNumLoadedDrawables;
+ startDrawableCount = sPreloadTracingNumLoadedDrawables;
}
if (DEBUG_LOAD) {
Log.v(TAG, "Loading drawable for cookie " + value.assetCookie + ": " + file);
}
+
final Drawable dr;
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
+ LookupStack stack = mLookupStack.get();
try {
- if (file.endsWith(".xml")) {
- final XmlResourceParser rp = loadXmlResourceParser(
- file, id, value.assetCookie, "drawable");
- dr = Drawable.createFromXmlForDensity(wrapper, rp, density, theme);
- rp.close();
- } else {
- final InputStream is = mAssets.openNonAsset(
- value.assetCookie, file, AssetManager.ACCESS_STREAMING);
- dr = Drawable.createFromResourceStream(wrapper, value, is, file, null);
- is.close();
+ // Perform a linear search to check if we have already referenced this resource before.
+ if (stack.contains(id)) {
+ throw new Exception("Recursive reference in drawable");
+ }
+ stack.push(id);
+ try {
+ if (file.endsWith(".xml")) {
+ final XmlResourceParser rp = loadXmlResourceParser(
+ file, id, value.assetCookie, "drawable");
+ dr = Drawable.createFromXmlForDensity(wrapper, rp, density, null);
+ rp.close();
+ } else {
+ final InputStream is = mAssets.openNonAsset(
+ value.assetCookie, file, AssetManager.ACCESS_STREAMING);
+ AssetInputStream ais = (AssetInputStream) is;
+ dr = decodeImageDrawable(ais, wrapper, value);
+ }
+ } finally {
+ stack.pop();
}
} catch (Exception | StackOverflowError e) {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
@@ -815,7 +860,7 @@ public class ResourcesImpl {
final long loadedBitmapSize =
Bitmap.sPreloadTracingTotalBitmapsSize - startBitmapSize;
final int loadedDrawables =
- sPreloadTracingNumLoadedDrawables - startDrwableCount;
+ sPreloadTracingNumLoadedDrawables - startDrawableCount;
sPreloadTracingNumLoadedDrawables++;
@@ -891,6 +936,7 @@ public class ResourcesImpl {
* first try to load CSL from the cache. If not found, try to get from the constant state.
* Last, parse the XML and generate the CSL.
*/
+ @Nullable
private ComplexColor loadComplexColorFromName(Resources wrapper, Resources.Theme theme,
TypedValue value, int id) {
final long key = (((long) value.assetCookie) << 32) | value.data;
@@ -966,7 +1012,7 @@ public class ResourcesImpl {
return complexColor;
}
- @Nullable
+ @NonNull
ColorStateList loadColorStateList(Resources wrapper, TypedValue value, int id,
Resources.Theme theme)
throws NotFoundException {
@@ -1024,9 +1070,10 @@ public class ResourcesImpl {
* We deferred the parser creation to this function b/c we need to differentiate b/t gradient
* and selector tag.
*
- * @return a ComplexColor (GradientColor or ColorStateList) based on the XML file content.
+ * @return a ComplexColor (GradientColor or ColorStateList) based on the XML file content, or
+ * {@code null} if the XML file is neither.
*/
- @Nullable
+ @NonNull
private ComplexColor loadComplexColorForCookie(Resources wrapper, TypedValue value, int id,
Resources.Theme theme) {
if (value.string == null) {
@@ -1255,8 +1302,7 @@ public class ResourcesImpl {
void applyStyle(int resId, boolean force) {
synchronized (mKey) {
- AssetManager.applyThemeStyle(mTheme, resId, force);
-
+ mAssets.applyStyleToTheme(mTheme, resId, force);
mThemeResId = resId;
mKey.append(resId, force);
}
@@ -1265,7 +1311,7 @@ public class ResourcesImpl {
void setTo(ThemeImpl other) {
synchronized (mKey) {
synchronized (other.mKey) {
- AssetManager.copyTheme(mTheme, other.mTheme);
+ AssetManager.nativeThemeCopy(mTheme, other.mTheme);
mThemeResId = other.mThemeResId;
mKey.setTo(other.getKey());
@@ -1288,12 +1334,10 @@ public class ResourcesImpl {
// out the attributes from the XML file (applying type information
// contained in the resources and such).
final XmlBlock.Parser parser = (XmlBlock.Parser) set;
- AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes,
- parser != null ? parser.mParseState : 0,
- attrs, attrs.length, array.mDataAddress, array.mIndicesAddress);
+ mAssets.applyStyle(mTheme, defStyleAttr, defStyleRes, parser, attrs,
+ array.mDataAddress, array.mIndicesAddress);
array.mTheme = wrapper;
array.mXml = parser;
-
return array;
}
}
@@ -1310,7 +1354,7 @@ public class ResourcesImpl {
}
final TypedArray array = TypedArray.obtain(wrapper.getResources(), len);
- AssetManager.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices);
+ mAssets.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices);
array.mTheme = wrapper;
array.mXml = null;
return array;
@@ -1330,14 +1374,14 @@ public class ResourcesImpl {
@Config int getChangingConfigurations() {
synchronized (mKey) {
final @NativeConfig int nativeChangingConfig =
- AssetManager.getThemeChangingConfigurations(mTheme);
+ AssetManager.nativeThemeGetChangingConfigurations(mTheme);
return ActivityInfo.activityInfoConfigNativeToJava(nativeChangingConfig);
}
}
public void dump(int priority, String tag, String prefix) {
synchronized (mKey) {
- AssetManager.dumpTheme(mTheme, priority, tag, prefix);
+ mAssets.dumpTheme(mTheme, priority, tag, prefix);
}
}
@@ -1366,15 +1410,40 @@ public class ResourcesImpl {
*/
void rebase() {
synchronized (mKey) {
- AssetManager.clearTheme(mTheme);
+ AssetManager.nativeThemeClear(mTheme);
// Reapply the same styles in the same order.
for (int i = 0; i < mKey.mCount; i++) {
final int resId = mKey.mResId[i];
final boolean force = mKey.mForce[i];
- AssetManager.applyThemeStyle(mTheme, resId, force);
+ mAssets.applyStyleToTheme(mTheme, resId, force);
}
}
}
}
+
+ private static class LookupStack {
+
+ // Pick a reasonable default size for the array, it is grown as needed.
+ private int[] mIds = new int[4];
+ private int mSize = 0;
+
+ public void push(int id) {
+ mIds = GrowingArrayUtils.append(mIds, mSize, id);
+ mSize++;
+ }
+
+ public boolean contains(int id) {
+ for (int i = 0; i < mSize; i++) {
+ if (mIds[i] == id) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void pop() {
+ mSize--;
+ }
+ }
}
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index f33c75168a5f..cbb3c6df0558 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -61,6 +61,15 @@ public class TypedArray {
return attrs;
}
+ // STYLE_ prefixed constants are offsets within the typed data array.
+ static final int STYLE_NUM_ENTRIES = 6;
+ static final int STYLE_TYPE = 0;
+ static final int STYLE_DATA = 1;
+ static final int STYLE_ASSET_COOKIE = 2;
+ static final int STYLE_RESOURCE_ID = 3;
+ static final int STYLE_CHANGING_CONFIGURATIONS = 4;
+ static final int STYLE_DENSITY = 5;
+
private final Resources mResources;
private DisplayMetrics mMetrics;
private AssetManager mAssets;
@@ -78,7 +87,7 @@ public class TypedArray {
private void resize(int len) {
mLength = len;
- final int dataLen = len * AssetManager.STYLE_NUM_ENTRIES;
+ final int dataLen = len * STYLE_NUM_ENTRIES;
final int indicesLen = len + 1;
final VMRuntime runtime = VMRuntime.getRuntime();
if (mDataAddress == 0 || mData.length < dataLen) {
@@ -166,9 +175,9 @@ public class TypedArray {
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return null;
} else if (type == TypedValue.TYPE_STRING) {
@@ -203,9 +212,9 @@ public class TypedArray {
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return null;
} else if (type == TypedValue.TYPE_STRING) {
@@ -242,14 +251,13 @@ public class TypedArray {
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_STRING) {
- final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
+ final int cookie = data[index + STYLE_ASSET_COOKIE];
if (cookie < 0) {
- return mXml.getPooledString(
- data[index+AssetManager.STYLE_DATA]).toString();
+ return mXml.getPooledString(data[index + STYLE_DATA]).toString();
}
}
return null;
@@ -274,11 +282,11 @@ public class TypedArray {
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
final @Config int changingConfigs = ActivityInfo.activityInfoConfigNativeToJava(
- data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]);
+ data[index + STYLE_CHANGING_CONFIGURATIONS]);
if ((changingConfigs & ~allowedChangingConfigs) != 0) {
return null;
}
@@ -320,14 +328,14 @@ public class TypedArray {
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type >= TypedValue.TYPE_FIRST_INT
&& type <= TypedValue.TYPE_LAST_INT) {
- return data[index+AssetManager.STYLE_DATA] != 0;
+ return data[index + STYLE_DATA] != 0;
}
final TypedValue v = mValue;
@@ -359,14 +367,14 @@ public class TypedArray {
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type >= TypedValue.TYPE_FIRST_INT
&& type <= TypedValue.TYPE_LAST_INT) {
- return data[index+AssetManager.STYLE_DATA];
+ return data[index + STYLE_DATA];
}
final TypedValue v = mValue;
@@ -396,16 +404,16 @@ public class TypedArray {
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type == TypedValue.TYPE_FLOAT) {
- return Float.intBitsToFloat(data[index+AssetManager.STYLE_DATA]);
+ return Float.intBitsToFloat(data[index + STYLE_DATA]);
} else if (type >= TypedValue.TYPE_FIRST_INT
&& type <= TypedValue.TYPE_LAST_INT) {
- return data[index+AssetManager.STYLE_DATA];
+ return data[index + STYLE_DATA];
}
final TypedValue v = mValue;
@@ -446,15 +454,15 @@ public class TypedArray {
}
final int attrIndex = index;
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type >= TypedValue.TYPE_FIRST_INT
&& type <= TypedValue.TYPE_LAST_INT) {
- return data[index+AssetManager.STYLE_DATA];
+ return data[index + STYLE_DATA];
} else if (type == TypedValue.TYPE_STRING) {
final TypedValue value = mValue;
if (getValueAt(index, value)) {
@@ -498,7 +506,7 @@ public class TypedArray {
}
final TypedValue value = mValue;
- if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+ if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
if (value.type == TypedValue.TYPE_ATTRIBUTE) {
throw new UnsupportedOperationException(
"Failed to resolve attribute at index " + index + ": " + value);
@@ -533,7 +541,7 @@ public class TypedArray {
}
final TypedValue value = mValue;
- if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+ if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
if (value.type == TypedValue.TYPE_ATTRIBUTE) {
throw new UnsupportedOperationException(
"Failed to resolve attribute at index " + index + ": " + value);
@@ -564,15 +572,15 @@ public class TypedArray {
}
final int attrIndex = index;
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type >= TypedValue.TYPE_FIRST_INT
&& type <= TypedValue.TYPE_LAST_INT) {
- return data[index+AssetManager.STYLE_DATA];
+ return data[index + STYLE_DATA];
} else if (type == TypedValue.TYPE_ATTRIBUTE) {
final TypedValue value = mValue;
getValueAt(index, value);
@@ -612,15 +620,14 @@ public class TypedArray {
}
final int attrIndex = index;
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type == TypedValue.TYPE_DIMENSION) {
- return TypedValue.complexToDimension(
- data[index + AssetManager.STYLE_DATA], mMetrics);
+ return TypedValue.complexToDimension(data[index + STYLE_DATA], mMetrics);
} else if (type == TypedValue.TYPE_ATTRIBUTE) {
final TypedValue value = mValue;
getValueAt(index, value);
@@ -661,15 +668,14 @@ public class TypedArray {
}
final int attrIndex = index;
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type == TypedValue.TYPE_DIMENSION) {
- return TypedValue.complexToDimensionPixelOffset(
- data[index + AssetManager.STYLE_DATA], mMetrics);
+ return TypedValue.complexToDimensionPixelOffset(data[index + STYLE_DATA], mMetrics);
} else if (type == TypedValue.TYPE_ATTRIBUTE) {
final TypedValue value = mValue;
getValueAt(index, value);
@@ -711,15 +717,14 @@ public class TypedArray {
}
final int attrIndex = index;
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type == TypedValue.TYPE_DIMENSION) {
- return TypedValue.complexToDimensionPixelSize(
- data[index+AssetManager.STYLE_DATA], mMetrics);
+ return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics);
} else if (type == TypedValue.TYPE_ATTRIBUTE) {
final TypedValue value = mValue;
getValueAt(index, value);
@@ -755,16 +760,15 @@ public class TypedArray {
}
final int attrIndex = index;
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type >= TypedValue.TYPE_FIRST_INT
&& type <= TypedValue.TYPE_LAST_INT) {
- return data[index+AssetManager.STYLE_DATA];
+ return data[index + STYLE_DATA];
} else if (type == TypedValue.TYPE_DIMENSION) {
- return TypedValue.complexToDimensionPixelSize(
- data[index+AssetManager.STYLE_DATA], mMetrics);
+ return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics);
} else if (type == TypedValue.TYPE_ATTRIBUTE) {
final TypedValue value = mValue;
getValueAt(index, value);
@@ -795,15 +799,14 @@ public class TypedArray {
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type >= TypedValue.TYPE_FIRST_INT
&& type <= TypedValue.TYPE_LAST_INT) {
- return data[index+AssetManager.STYLE_DATA];
+ return data[index + STYLE_DATA];
} else if (type == TypedValue.TYPE_DIMENSION) {
- return TypedValue.complexToDimensionPixelSize(
- data[index + AssetManager.STYLE_DATA], mMetrics);
+ return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics);
}
return defValue;
@@ -833,15 +836,14 @@ public class TypedArray {
}
final int attrIndex = index;
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type == TypedValue.TYPE_FRACTION) {
- return TypedValue.complexToFraction(
- data[index+AssetManager.STYLE_DATA], base, pbase);
+ return TypedValue.complexToFraction(data[index + STYLE_DATA], base, pbase);
} else if (type == TypedValue.TYPE_ATTRIBUTE) {
final TypedValue value = mValue;
getValueAt(index, value);
@@ -874,10 +876,10 @@ public class TypedArray {
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- if (data[index+AssetManager.STYLE_TYPE] != TypedValue.TYPE_NULL) {
- final int resid = data[index+AssetManager.STYLE_RESOURCE_ID];
+ if (data[index + STYLE_TYPE] != TypedValue.TYPE_NULL) {
+ final int resid = data[index + STYLE_RESOURCE_ID];
if (resid != 0) {
return resid;
}
@@ -902,10 +904,10 @@ public class TypedArray {
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- if (data[index + AssetManager.STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) {
- return data[index + AssetManager.STYLE_DATA];
+ if (data[index + STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) {
+ return data[index + STYLE_DATA];
}
return defValue;
}
@@ -939,7 +941,7 @@ public class TypedArray {
}
final TypedValue value = mValue;
- if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+ if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
if (value.type == TypedValue.TYPE_ATTRIBUTE) {
throw new UnsupportedOperationException(
"Failed to resolve attribute at index " + index + ": " + value);
@@ -975,7 +977,7 @@ public class TypedArray {
}
final TypedValue value = mValue;
- if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+ if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
if (value.type == TypedValue.TYPE_ATTRIBUTE) {
throw new UnsupportedOperationException(
"Failed to resolve attribute at index " + index + ": " + value);
@@ -1006,7 +1008,7 @@ public class TypedArray {
}
final TypedValue value = mValue;
- if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+ if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
return mResources.getTextArray(value.resourceId);
}
return null;
@@ -1027,7 +1029,7 @@ public class TypedArray {
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- return getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, outValue);
+ return getValueAt(index * STYLE_NUM_ENTRIES, outValue);
}
/**
@@ -1043,8 +1045,8 @@ public class TypedArray {
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
- return mData[index + AssetManager.STYLE_TYPE];
+ index *= STYLE_NUM_ENTRIES;
+ return mData[index + STYLE_TYPE];
}
/**
@@ -1063,9 +1065,9 @@ public class TypedArray {
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
return type != TypedValue.TYPE_NULL;
}
@@ -1084,11 +1086,11 @@ public class TypedArray {
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
return type != TypedValue.TYPE_NULL
- || data[index+AssetManager.STYLE_DATA] == TypedValue.DATA_NULL_EMPTY;
+ || data[index + STYLE_DATA] == TypedValue.DATA_NULL_EMPTY;
}
/**
@@ -1109,7 +1111,7 @@ public class TypedArray {
}
final TypedValue value = mValue;
- if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+ if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
return value;
}
return null;
@@ -1181,16 +1183,16 @@ public class TypedArray {
final int[] data = mData;
final int N = length();
for (int i = 0; i < N; i++) {
- final int index = i * AssetManager.STYLE_NUM_ENTRIES;
- if (data[index + AssetManager.STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) {
+ final int index = i * STYLE_NUM_ENTRIES;
+ if (data[index + STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) {
// Not an attribute, ignore.
continue;
}
// Null the entry so that we can safely call getZzz().
- data[index + AssetManager.STYLE_TYPE] = TypedValue.TYPE_NULL;
+ data[index + STYLE_TYPE] = TypedValue.TYPE_NULL;
- final int attr = data[index + AssetManager.STYLE_DATA];
+ final int attr = data[index + STYLE_DATA];
if (attr == 0) {
// Useless data, ignore.
continue;
@@ -1231,45 +1233,44 @@ public class TypedArray {
final int[] data = mData;
final int N = length();
for (int i = 0; i < N; i++) {
- final int index = i * AssetManager.STYLE_NUM_ENTRIES;
- final int type = data[index + AssetManager.STYLE_TYPE];
+ final int index = i * STYLE_NUM_ENTRIES;
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
continue;
}
changingConfig |= ActivityInfo.activityInfoConfigNativeToJava(
- data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]);
+ data[index + STYLE_CHANGING_CONFIGURATIONS]);
}
return changingConfig;
}
private boolean getValueAt(int index, TypedValue outValue) {
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return false;
}
outValue.type = type;
- outValue.data = data[index+AssetManager.STYLE_DATA];
- outValue.assetCookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
- outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID];
+ outValue.data = data[index + STYLE_DATA];
+ outValue.assetCookie = data[index + STYLE_ASSET_COOKIE];
+ outValue.resourceId = data[index + STYLE_RESOURCE_ID];
outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
- data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]);
- outValue.density = data[index+AssetManager.STYLE_DENSITY];
+ data[index + STYLE_CHANGING_CONFIGURATIONS]);
+ outValue.density = data[index + STYLE_DENSITY];
outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null;
return true;
}
private CharSequence loadStringValueAt(int index) {
final int[] data = mData;
- final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
+ final int cookie = data[index + STYLE_ASSET_COOKIE];
if (cookie < 0) {
if (mXml != null) {
- return mXml.getPooledString(
- data[index+AssetManager.STYLE_DATA]);
+ return mXml.getPooledString(data[index + STYLE_DATA]);
}
return null;
}
- return mAssets.getPooledStringForCookie(cookie, data[index+AssetManager.STYLE_DATA]);
+ return mAssets.getPooledStringForCookie(cookie, data[index + STYLE_DATA]);
}
/** @hide */
diff --git a/core/java/android/content/res/XmlBlock.java b/core/java/android/content/res/XmlBlock.java
index e6b957414ea8..d4ccffb83ca5 100644
--- a/core/java/android/content/res/XmlBlock.java
+++ b/core/java/android/content/res/XmlBlock.java
@@ -16,6 +16,7 @@
package android.content.res;
+import android.annotation.Nullable;
import android.util.TypedValue;
import com.android.internal.util.XmlUtils;
@@ -33,7 +34,7 @@ import java.io.Reader;
*
* {@hide}
*/
-final class XmlBlock {
+final class XmlBlock implements AutoCloseable {
private static final boolean DEBUG=false;
public XmlBlock(byte[] data) {
@@ -48,6 +49,7 @@ final class XmlBlock {
mStrings = new StringBlock(nativeGetStringBlock(mNative), false);
}
+ @Override
public void close() {
synchronized (this) {
if (mOpen) {
@@ -478,13 +480,13 @@ final class XmlBlock {
* are doing! The given native object must exist for the entire lifetime
* of this newly creating XmlBlock.
*/
- XmlBlock(AssetManager assets, long xmlBlock) {
+ XmlBlock(@Nullable AssetManager assets, long xmlBlock) {
mAssets = assets;
mNative = xmlBlock;
mStrings = new StringBlock(nativeGetStringBlock(xmlBlock), false);
}
- private final AssetManager mAssets;
+ private @Nullable final AssetManager mAssets;
private final long mNative;
/*package*/ final StringBlock mStrings;
private boolean mOpen = true;
diff --git a/core/java/android/content/res/XmlResourceParser.java b/core/java/android/content/res/XmlResourceParser.java
index 6be9b9eb0438..86f4ba6189de 100644
--- a/core/java/android/content/res/XmlResourceParser.java
+++ b/core/java/android/content/res/XmlResourceParser.java
@@ -27,6 +27,8 @@ import org.xmlpull.v1.XmlPullParser;
* it is done reading the resource.
*/
public interface XmlResourceParser extends XmlPullParser, AttributeSet, AutoCloseable {
+ String getAttributeNamespace (int index);
+
/**
* Close this parser. Calls on the interface are no longer valid after this call.
*/