diff options
| author | Xin Li <delphij@google.com> | 2018-08-06 19:19:55 -0700 |
|---|---|---|
| committer | Xin Li <delphij@google.com> | 2018-08-06 19:20:02 -0700 |
| commit | 02857a72198613a0583cdf6863edb2df59beee04 (patch) | |
| tree | 076fcfdb52deea3aada1c0dd8b31decbd87c80b0 /core/java/android/content | |
| parent | 10c593cf7f30ddea32889361c81ef06eabaeb6b3 (diff) | |
| parent | 7e1e76a6dc088458b159c4c89c54b78d32d7d310 (diff) | |
Merge Android Pie into master
Bug: 112104996
Change-Id: Id91836f22f2c9842975ac5b55f0f18b89db9b2f5
Diffstat (limited to 'core/java/android/content')
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 * * @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> <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 <activity> and * <receiver> 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 <manifest> + * 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 <manifest> * 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 <manifest> + * 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 <manifest> * 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. ß -> ß + // - 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—activities that handle the + * {@link Intent#ACTION_MAIN} action and the {@link Intent#CATEGORY_LAUNCHER} category—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—activities that handle the {@code MAIN} action and the - * {@code LAUNCHER} category—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 <item> + * elements in + * a <style> 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. */ |
