diff options
Diffstat (limited to 'core/java')
235 files changed, 6214 insertions, 2878 deletions
diff --git a/core/java/Android.bp b/core/java/Android.bp index f7c5c57a07e4..fb27f74211fb 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -7,33 +7,3 @@ filegroup { name: "IDropBoxManagerService.aidl", srcs: ["com/android/internal/os/IDropBoxManagerService.aidl"], } - -// only used by key_store_service -cc_library_shared { - name: "libkeystore_aidl", - srcs: ["android/security/IKeystoreService.aidl", - "android/security/IConfirmationPromptCallback.aidl"], - aidl: { - export_aidl_headers: true, - include_dirs: [ - "frameworks/base/core/java/", - "system/security/keystore/", - ], - }, - shared_libs: [ - "libbinder", - "libcutils", - "libhardware", - "libhidlbase", - "libhidltransport", - "libhwbinder", - "liblog", - "libkeystore_parcelables", - "libselinux", - "libutils", - ], - export_shared_lib_headers: [ - "libbinder", - "libkeystore_parcelables", - ], -} diff --git a/core/java/android/accounts/OWNERS b/core/java/android/accounts/OWNERS new file mode 100644 index 000000000000..ea5fd36702f9 --- /dev/null +++ b/core/java/android/accounts/OWNERS @@ -0,0 +1,10 @@ +carlosvaldivia@google.com +dementyev@google.com +sandrakwan@google.com +hackbod@google.com +svetoslavganov@google.com +moltmann@google.com +fkupolov@google.com +yamasani@google.com +omakoto@google.com + diff --git a/core/java/android/annotation/OWNERS b/core/java/android/annotation/OWNERS new file mode 100644 index 000000000000..d6bb71b50e34 --- /dev/null +++ b/core/java/android/annotation/OWNERS @@ -0,0 +1 @@ +tnorbye@google.com diff --git a/core/java/android/annotation/RequiresFeature.java b/core/java/android/annotation/RequiresFeature.java new file mode 100644 index 000000000000..fc93f03d76cf --- /dev/null +++ b/core/java/android/annotation/RequiresFeature.java @@ -0,0 +1,45 @@ +/* + * 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.annotation; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.content.pm.PackageManager; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Denotes that the annotated element requires one or more device features. This + * is used to auto-generate documentation. + * + * @see PackageManager#hasSystemFeature(String) + * @hide + */ +@Retention(SOURCE) +@Target({TYPE,FIELD,METHOD,CONSTRUCTOR}) +public @interface RequiresFeature { + /** + * The name of the device feature that is required. + * + * @see PackageManager#hasSystemFeature(String) + */ + String value(); +} diff --git a/core/java/android/annotation/SystemService.java b/core/java/android/annotation/SystemService.java index ba5002a4f1b5..0c5d15e178a3 100644 --- a/core/java/android/annotation/SystemService.java +++ b/core/java/android/annotation/SystemService.java @@ -26,12 +26,19 @@ import java.lang.annotation.Target; /** * Description of a system service available through - * {@link Context#getSystemService(Class)}. + * {@link Context#getSystemService(Class)}. This is used to auto-generate + * documentation explaining how to obtain a reference to the service. * * @hide */ @Retention(SOURCE) @Target(TYPE) public @interface SystemService { + /** + * The string name of the system service that can be passed to + * {@link Context#getSystemService(String)}. + * + * @see Context#getSystemServiceName(Class) + */ String value(); } diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 0bc510a13ba6..83fe4dd0038a 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -17,6 +17,7 @@ package android.app; import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS; + import static java.lang.Character.MIN_VALUE; import android.annotation.CallSuper; @@ -135,6 +136,7 @@ import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -1733,7 +1735,7 @@ public class Activity extends ContextThemeWrapper * * <p>This callback and {@link #onUserInteraction} are intended to help * activities manage status bar notifications intelligently; specifically, - * for helping activities determine the proper time to cancel a notfication. + * for helping activities determine the proper time to cancel a notification. * * @see #onUserInteraction() */ @@ -1741,32 +1743,16 @@ public class Activity extends ContextThemeWrapper } /** - * Generate a new thumbnail for this activity. This method is called before - * pausing the activity, and should draw into <var>outBitmap</var> the - * imagery for the desired thumbnail in the dimensions of that bitmap. It - * can use the given <var>canvas</var>, which is configured to draw into the - * bitmap, for rendering if desired. - * - * <p>The default implementation returns fails and does not draw a thumbnail; - * this will result in the platform creating its own thumbnail if needed. - * - * @param outBitmap The bitmap to contain the thumbnail. - * @param canvas Can be used to render into the bitmap. - * - * @return Return true if you have drawn into the bitmap; otherwise after - * you return it will be filled with a default thumbnail. - * - * @see #onCreateDescription - * @see #onSaveInstanceState - * @see #onPause + * @deprecated Method doesn't do anything and will be removed in the future. */ + @Deprecated public boolean onCreateThumbnail(Bitmap outBitmap, Canvas canvas) { return false; } /** * Generate a new description for this activity. This method is called - * before pausing the activity and can, if desired, return some textual + * before stopping the activity and can, if desired, return some textual * description of its current state to be displayed to the user. * * <p>The default implementation returns null, which will cause you to @@ -1777,9 +1763,8 @@ public class Activity extends ContextThemeWrapper * @return A description of what the user is doing. It should be short and * sweet (only a few words). * - * @see #onCreateThumbnail * @see #onSaveInstanceState - * @see #onPause + * @see #onStop */ @Nullable public CharSequence onCreateDescription() { @@ -1915,7 +1900,7 @@ public class Activity extends ContextThemeWrapper if (isFinishing()) { if (mAutoFillResetNeeded) { - getAutofillManager().onActivityFinished(); + getAutofillManager().onActivityFinishing(); } else if (mIntent != null && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)) { // Activity was launched when user tapped a link in the Autofill Save UI - since @@ -6358,6 +6343,8 @@ public class Activity extends ContextThemeWrapper final AutofillManager afm = getAutofillManager(); if (afm != null) { + writer.print(prefix); writer.print("Autofill Compat Mode: "); + writer.println(isAutofillCompatibilityEnabled()); afm.dump(prefix, writer); } else { writer.print(prefix); writer.println("No AutofillManager"); @@ -7179,8 +7166,8 @@ public class Activity extends ContextThemeWrapper String appName = getApplicationInfo().loadLabel(getPackageManager()) .toString(); - String warning = "Detected problems with API compatiblity\n" - + "(please consult log for detail)"; + String warning = "Detected problems with API compatibility\n" + + "(visit g.co/dev/appcompat for more info)"; if (isAppDebuggable) { new AlertDialog.Builder(this) .setTitle(appName) @@ -7706,6 +7693,9 @@ public class Activity extends ContextThemeWrapper } } } + if (android.view.autofill.Helper.sVerbose) { + Log.v(TAG, "autofillClientGetViewVisibility(): " + Arrays.toString(visible)); + } return visible; } @@ -7768,7 +7758,6 @@ public class Activity extends ContextThemeWrapper * @param disable {@code true} to disable preview screenshots; {@code false} otherwise. * @hide */ - @SystemApi public void setDisablePreviewScreenshots(boolean disable) { try { ActivityManager.getService().setDisablePreviewScreenshots(mToken, disable); diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index ae47a684f6ab..03faeeeb91a1 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -44,6 +44,7 @@ import android.graphics.GraphicBuffer; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; +import android.net.Uri; import android.os.BatteryStats; import android.os.Binder; import android.os.Build; @@ -2750,6 +2751,30 @@ public class ActivityManager { } /** + * Updates (grants or revokes) a persitable URI permission. + * + * @param uri URI to be granted or revoked. + * @param prefix if {@code false}, permission apply to this specific URI; if {@code true}, it + * applies to all URIs that are prefixed by this URI. + * @param packageName target package. + * @param grant if {@code true} a new permission will be granted, otherwise an existing + * permission will be revoked. + * + * @return whether or not the requested succeeded. + * + * @hide + */ + public boolean updatePersistableUriPermission(Uri uri, boolean prefix, String packageName, + boolean grant) { + try { + return getService().updatePersistableUriPermission(uri, prefix, packageName, grant, + UserHandle.myUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Information you can retrieve about any processes that are in an error condition. */ public static class ProcessErrorStateInfo implements Parcelable { diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 5ee7edee9db7..0c98267cfd68 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -219,6 +219,9 @@ public abstract class ActivityManagerInternal { /** * Start activity {@code intents} as if {@code packageName} on user {@code userId} did it. * + * - DO NOT call it with the calling UID cleared. + * - All the necessary caller permission checks must be done at callsites. + * * @return error codes used by {@link IActivityManager#startActivity} and its siblings. */ public abstract int startActivitiesAsPackage(String packageName, @@ -348,4 +351,14 @@ public abstract class ActivityManagerInternal { * Returns is the caller has the same uid as the Recents component */ public abstract boolean isCallerRecents(int callingUid); + + /** + * Whether an UID is active or idle. + */ + public abstract boolean isUidActive(int uid); + + /** + * Returns a list that contains the memory stats for currently running processes. + */ + public abstract List<ProcessMemoryState> getMemoryStateForProcesses(); } diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index fee58274a5fc..d5430f05d65b 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -1106,6 +1106,11 @@ public class ActivityOptions { } /** @hide */ + public void setRemoteAnimationAdapter(RemoteAnimationAdapter remoteAnimationAdapter) { + mRemoteAnimationAdapter = remoteAnimationAdapter; + } + + /** @hide */ public static ActivityOptions fromBundle(Bundle bOptions) { return bOptions != null ? new ActivityOptions(bOptions) : null; } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 45c57ca67c87..a96f484ccf11 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -113,6 +113,7 @@ import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; import android.util.LogPrinter; +import android.util.MergedConfiguration; import android.util.Pair; import android.util.PrintWriterPrinter; import android.util.Slog; @@ -166,9 +167,9 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.InetAddress; import java.text.DateFormat; -import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; +import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; @@ -204,7 +205,7 @@ public final class ActivityThread extends ClientTransactionHandler { private static final boolean DEBUG_SERVICE = false; public static final boolean DEBUG_MEMORY_TRIM = false; private static final boolean DEBUG_PROVIDER = false; - private static final boolean DEBUG_ORDER = false; + public static final boolean DEBUG_ORDER = false; private static final long MIN_TIME_BETWEEN_GCS = 5*1000; private static final int SQLITE_MEM_RELEASED_EVENT_LOG_TAG = 75003; private static final int LOG_AM_ON_PAUSE_CALLED = 30021; @@ -222,7 +223,7 @@ public final class ActivityThread extends ClientTransactionHandler { private static final boolean REPORT_TO_ACTIVITY = true; // Maximum number of recent tokens to maintain for debugging purposes - private static final int MAX_RECENT_TOKENS = 10; + private static final int MAX_DESTROYED_ACTIVITIES = 10; /** * Denotes an invalid sequence number corresponding to a process state change. @@ -256,7 +257,7 @@ public final class ActivityThread extends ClientTransactionHandler { final H mH = new H(); final Executor mExecutor = new HandlerExecutor(mH); final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>(); - final ArrayDeque<Integer> mRecentTokens = new ArrayDeque<>(); + final ArrayList<DestroyedActivityInfo> mRecentDestroyedActivities = new ArrayList<>(); // List of new activities (via ActivityRecord.nextIdle) that should // be reported when next we idle. @@ -339,6 +340,26 @@ public final class ActivityThread extends ClientTransactionHandler { } } + /** + * TODO(b/71506345): Remove this once bug is resolved. + */ + private static final class DestroyedActivityInfo { + private final Integer mToken; + private final String mReason; + private final long mTime; + + DestroyedActivityInfo(Integer token, String reason) { + mToken = token; + mReason = reason; + mTime = System.currentTimeMillis(); + } + + void dump(PrintWriter pw, String prefix) { + pw.println(prefix + "[token:" + mToken + " | time:" + mTime + " | reason:" + mReason + + "]"); + } + } + // The lock of mProviderMap protects the following variables. final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap = new ArrayMap<ProviderKey, ProviderClientRecord>(); @@ -398,7 +419,6 @@ public final class ActivityThread extends ClientTransactionHandler { boolean startsNotResumed; public final boolean isForward; int pendingConfigChanges; - boolean onlyLocalRequest; Window mPendingRemoveWindow; WindowManager mPendingRemoveWindowManager; @@ -520,7 +540,6 @@ public final class ActivityThread extends ClientTransactionHandler { sb.append(", startsNotResumed=").append(startsNotResumed); sb.append(", isForward=").append(isForward); sb.append(", pendingConfigChanges=").append(pendingConfigChanges); - sb.append(", onlyLocalRequest=").append(onlyLocalRequest); sb.append(", preserveWindow=").append(mPreserveWindow); if (activity != null) { sb.append(", Activity{"); @@ -765,15 +784,6 @@ public final class ActivityThread extends ClientTransactionHandler { sendMessage(H.SLEEPING, token, sleeping ? 1 : 0); } - @Override - public final void scheduleRelaunchActivity(IBinder token, - List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, - int configChanges, boolean notResumed, Configuration config, - Configuration overrideConfig, boolean preserveWindow) { - requestRelaunchActivity(token, pendingResults, pendingNewIntents, - configChanges, notResumed, config, overrideConfig, true, preserveWindow); - } - public final void scheduleReceiver(Intent intent, ActivityInfo info, CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras, boolean sync, int sendingUser, int processState) { @@ -1531,7 +1541,6 @@ public final class ActivityThread extends ClientTransactionHandler { public static final int UNBIND_SERVICE = 122; public static final int DUMP_SERVICE = 123; public static final int LOW_MEMORY = 124; - public static final int RELAUNCH_ACTIVITY = 126; public static final int PROFILER_CONTROL = 127; public static final int CREATE_BACKUP_AGENT = 128; public static final int DESTROY_BACKUP_AGENT = 129; @@ -1577,7 +1586,6 @@ public final class ActivityThread extends ClientTransactionHandler { case UNBIND_SERVICE: return "UNBIND_SERVICE"; case DUMP_SERVICE: return "DUMP_SERVICE"; case LOW_MEMORY: return "LOW_MEMORY"; - case RELAUNCH_ACTIVITY: return "RELAUNCH_ACTIVITY"; case PROFILER_CONTROL: return "PROFILER_CONTROL"; case CREATE_BACKUP_AGENT: return "CREATE_BACKUP_AGENT"; case DESTROY_BACKUP_AGENT: return "DESTROY_BACKUP_AGENT"; @@ -1611,12 +1619,6 @@ public final class ActivityThread extends ClientTransactionHandler { public void handleMessage(Message msg) { if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what)); switch (msg.what) { - case RELAUNCH_ACTIVITY: { - Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart"); - ActivityClientRecord r = (ActivityClientRecord)msg.obj; - handleRelaunchActivity(r); - Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); - } break; case BIND_APPLICATION: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication"); AppBindData data = (AppBindData)msg.obj; @@ -2182,14 +2184,28 @@ public final class ActivityThread extends ClientTransactionHandler { @Override public void dump(PrintWriter pw, String prefix) { - pw.println(prefix + "mActivities:"); + pw.println(prefix + "Activities:"); - for (ArrayMap.Entry<IBinder, ActivityClientRecord> entry : mActivities.entrySet()) { - pw.println(prefix + " [token:" + entry.getKey().hashCode() + " record:" - + entry.getValue().toString() + "]"); + if (!mActivities.isEmpty()) { + final Iterator<Map.Entry<IBinder, ActivityClientRecord>> activitiesIterator = + mActivities.entrySet().iterator(); + + while (activitiesIterator.hasNext()) { + final ArrayMap.Entry<IBinder, ActivityClientRecord> entry = + activitiesIterator.next(); + pw.println(prefix + " [token:" + entry.getKey().hashCode() + " record:" + + entry.getValue().toString() + "]"); + } } - pw.println(prefix + "mRecentTokens:" + mRecentTokens); + if (!mRecentDestroyedActivities.isEmpty()) { + pw.println(prefix + "Recent destroyed activities:"); + for (int i = 0, size = mRecentDestroyedActivities.size(); i < size; i++) { + final DestroyedActivityInfo info = mRecentDestroyedActivities.get(i); + pw.print(prefix); + info.dump(pw, " "); + } + } } public static void dumpMemInfoTable(PrintWriter pw, Debug.MemoryInfo memInfo, boolean checkin, @@ -2876,11 +2892,6 @@ public final class ActivityThread extends ClientTransactionHandler { r.setState(ON_CREATE); mActivities.put(r.token, r); - mRecentTokens.push(r.token.hashCode()); - - if (mRecentTokens.size() > MAX_RECENT_TOKENS) { - mRecentTokens.removeLast(); - } } catch (SuperNotCalledException e) { throw e; @@ -3726,20 +3737,6 @@ public final class ActivityThread extends ClientTransactionHandler { } r.activity.performResume(r.startsNotResumed); - synchronized (mResourcesManager) { - // If there is a pending local relaunch that was requested when the activity was - // paused, it will put the activity into paused state when it finally happens. - // Since the activity resumed before being relaunched, we don't want that to - // happen, so we need to clear the request to relaunch paused. - for (int i = mRelaunchingActivities.size() - 1; i >= 0; i--) { - final ActivityClientRecord relaunching = mRelaunchingActivities.get(i); - if (relaunching.token == r.token - && relaunching.onlyLocalRequest && relaunching.startsNotResumed) { - relaunching.startsNotResumed = false; - } - } - } - EventLog.writeEvent(LOG_AM_ON_RESUME_CALLED, UserHandle.myUserId(), r.activity.getComponentName().getClassName(), reason); @@ -3888,14 +3885,12 @@ public final class ActivityThread extends ClientTransactionHandler { } } - if (!r.onlyLocalRequest) { - r.nextIdle = mNewActivities; - mNewActivities = r; - if (localLOGV) Slog.v( - TAG, "Scheduling idle handler for " + r); - Looper.myQueue().addIdleHandler(new Idler()); + r.nextIdle = mNewActivities; + mNewActivities = r; + if (localLOGV) { + Slog.v(TAG, "Scheduling idle handler for " + r); } - r.onlyLocalRequest = false; + Looper.myQueue().addIdleHandler(new Idler()); } else { // If an exception was thrown when trying to resume, then // just end this activity. @@ -3909,62 +3904,6 @@ public final class ActivityThread extends ClientTransactionHandler { } } - private int mThumbnailWidth = -1; - private int mThumbnailHeight = -1; - private Bitmap mAvailThumbnailBitmap = null; - private Canvas mThumbnailCanvas = null; - - private Bitmap createThumbnailBitmap(ActivityClientRecord r) { - Bitmap thumbnail = mAvailThumbnailBitmap; - try { - if (thumbnail == null) { - int w = mThumbnailWidth; - int h; - if (w < 0) { - Resources res = r.activity.getResources(); - int wId = com.android.internal.R.dimen.thumbnail_width; - int hId = com.android.internal.R.dimen.thumbnail_height; - mThumbnailWidth = w = res.getDimensionPixelSize(wId); - mThumbnailHeight = h = res.getDimensionPixelSize(hId); - } else { - h = mThumbnailHeight; - } - - // On platforms where we don't want thumbnails, set dims to (0,0) - if ((w > 0) && (h > 0)) { - thumbnail = Bitmap.createBitmap(r.activity.getResources().getDisplayMetrics(), - w, h, THUMBNAIL_FORMAT); - thumbnail.eraseColor(0); - } - } - - if (thumbnail != null) { - Canvas cv = mThumbnailCanvas; - if (cv == null) { - mThumbnailCanvas = cv = new Canvas(); - } - - cv.setBitmap(thumbnail); - if (!r.activity.onCreateThumbnail(thumbnail, cv)) { - mAvailThumbnailBitmap = thumbnail; - thumbnail = null; - } - cv.setBitmap(null); - } - - } catch (Exception e) { - if (!mInstrumentation.onException(r.activity, e)) { - throw new RuntimeException( - "Unable to create thumbnail of " - + r.intent.getComponent().toShortString() - + ": " + e.toString(), e); - } - thumbnail = null; - } - - return thumbnail; - } - @Override public void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving, int configChanges, boolean dontReport, PendingTransactionActions pendingActions) { @@ -4453,7 +4392,7 @@ public final class ActivityThread extends ClientTransactionHandler { /** Core implementation of activity destroy call. */ ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing, - int configChanges, boolean getNonConfigInstance) { + int configChanges, boolean getNonConfigInstance, String reason) { ActivityClientRecord r = mActivities.get(token); Class<? extends Activity> activityClass = null; if (localLOGV) Slog.v(TAG, "Performing finish of " + r); @@ -4505,6 +4444,12 @@ public final class ActivityThread extends ClientTransactionHandler { r.setState(ON_DESTROY); } mActivities.remove(token); + mRecentDestroyedActivities.add(0, new DestroyedActivityInfo(token.hashCode(), reason)); + + final int recentDestroyedActivitiesSize = mRecentDestroyedActivities.size(); + if (recentDestroyedActivitiesSize > MAX_DESTROYED_ACTIVITIES) { + mRecentDestroyedActivities.remove(recentDestroyedActivitiesSize - 1); + } StrictMode.decrementExpectedActivityCount(activityClass); return r; } @@ -4516,9 +4461,9 @@ public final class ActivityThread extends ClientTransactionHandler { @Override public void handleDestroyActivity(IBinder token, boolean finishing, int configChanges, - boolean getNonConfigInstance) { + boolean getNonConfigInstance, String reason) { ActivityClientRecord r = performDestroyActivity(token, finishing, - configChanges, getNonConfigInstance); + configChanges, getNonConfigInstance, reason); if (r != null) { cleanUpPendingRemoveWindows(r, finishing); WindowManager wm = r.activity.getWindowManager(); @@ -4586,15 +4531,12 @@ public final class ActivityThread extends ClientTransactionHandler { mSomeActivitiesChanged = true; } - /** - * @param preserveWindow Whether the activity should try to reuse the window it created, - * including the decor view after the relaunch. - */ - public final void requestRelaunchActivity(IBinder token, + @Override + public ActivityClientRecord prepareRelaunchActivity(IBinder token, List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, - int configChanges, boolean notResumed, Configuration config, - Configuration overrideConfig, boolean fromServer, boolean preserveWindow) { + int configChanges, MergedConfiguration config, boolean preserveWindow) { ActivityClientRecord target = null; + boolean scheduleRelaunch = false; synchronized (mResourcesManager) { for (int i=0; i<mRelaunchingActivities.size(); i++) { @@ -4616,57 +4558,31 @@ public final class ActivityThread extends ClientTransactionHandler { r.pendingIntents = pendingNewIntents; } } - - // For each relaunch request, activity manager expects an answer - if (!r.onlyLocalRequest && fromServer) { - try { - ActivityManager.getService().activityRelaunched(token); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } break; } } if (target == null) { - if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: target is null, fromServer:" - + fromServer); + if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: target is null"); target = new ActivityClientRecord(); target.token = token; target.pendingResults = pendingResults; target.pendingIntents = pendingNewIntents; target.mPreserveWindow = preserveWindow; - if (!fromServer) { - final ActivityClientRecord existing = mActivities.get(token); - if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: " + existing); - if (existing != null) { - if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: paused= " - + existing.paused);; - target.startsNotResumed = existing.paused; - target.overrideConfig = existing.overrideConfig; - } - target.onlyLocalRequest = true; - } mRelaunchingActivities.add(target); - sendMessage(H.RELAUNCH_ACTIVITY, target); - } - - if (fromServer) { - target.startsNotResumed = notResumed; - target.onlyLocalRequest = false; - } - if (config != null) { - target.createdConfig = config; - } - if (overrideConfig != null) { - target.overrideConfig = overrideConfig; + scheduleRelaunch = true; } + target.createdConfig = config.getGlobalConfiguration(); + target.overrideConfig = config.getOverrideConfiguration(); target.pendingConfigChanges |= configChanges; } + + return scheduleRelaunch ? target : null; } - private void handleRelaunchActivity(ActivityClientRecord tmp) { + @Override + public void handleRelaunchActivity(ActivityClientRecord tmp, + PendingTransactionActions pendingActions) { // If we are getting ready to gc after going to the background, well // we are back active so skip it. unscheduleGcIdler(); @@ -4735,18 +4651,10 @@ public final class ActivityThread extends ClientTransactionHandler { ActivityClientRecord r = mActivities.get(tmp.token); if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handling relaunch of " + r); if (r == null) { - if (!tmp.onlyLocalRequest) { - try { - ActivityManager.getService().activityRelaunched(tmp.token); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } return; } r.activity.mConfigChangeFlags |= configChanges; - r.onlyLocalRequest = tmp.onlyLocalRequest; r.mPreserveWindow = tmp.mPreserveWindow; r.activity.mChangingConfigurations = true; @@ -4763,9 +4671,9 @@ public final class ActivityThread extends ClientTransactionHandler { // preserved by the server, so we want to notify it that we are preparing to replace // everything try { - if (r.mPreserveWindow || r.onlyLocalRequest) { + if (r.mPreserveWindow) { WindowManagerGlobal.getWindowSession().prepareToReplaceWindows( - r.token, !r.onlyLocalRequest); + r.token, true /* childrenOnly */); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -4780,7 +4688,7 @@ public final class ActivityThread extends ClientTransactionHandler { callActivityOnStop(r, true /* saveState */, "handleRelaunchActivity"); } - handleDestroyActivity(r.token, false, configChanges, true); + handleDestroyActivity(r.token, false, configChanges, true, "handleRelaunchActivity"); r.activity = null; r.window = null; @@ -4804,24 +4712,22 @@ public final class ActivityThread extends ClientTransactionHandler { r.startsNotResumed = tmp.startsNotResumed; r.overrideConfig = tmp.overrideConfig; - // TODO(lifecycler): Move relaunch to lifecycler. - PendingTransactionActions pendingActions = new PendingTransactionActions(); handleLaunchActivity(r, pendingActions); - handleStartActivity(r, pendingActions); - handleResumeActivity(r.token, false /* clearHide */, r.isForward, "relaunch"); - if (r.startsNotResumed) { - performPauseActivity(r, false /* finished */, "relaunch", pendingActions); - } + // Only report a successful relaunch to WindowManager. + pendingActions.setReportRelaunchToWindowManager(true); + } - if (!tmp.onlyLocalRequest) { - try { - ActivityManager.getService().activityRelaunched(r.token); - if (r.window != null) { - r.window.reportActivityRelaunched(); - } - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + @Override + public void reportRelaunch(IBinder token, PendingTransactionActions pendingActions) { + try { + ActivityManager.getService().activityRelaunched(token); + final ActivityClientRecord r = mActivities.get(token); + if (pendingActions.shouldReportRelaunchToWindowManager() && r != null + && r.window != null) { + r.window.reportActivityRelaunched(); } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } } @@ -5897,21 +5803,23 @@ public final class ActivityThread extends ClientTransactionHandler { // Preload fonts resources FontsContract.setApplicationContextForResources(appContext); - try { - final ApplicationInfo info = - getPackageManager().getApplicationInfo( - data.appInfo.packageName, - PackageManager.GET_META_DATA /*flags*/, - UserHandle.myUserId()); - if (info.metaData != null) { - final int preloadedFontsResource = info.metaData.getInt( - ApplicationInfo.METADATA_PRELOADED_FONTS, 0); - if (preloadedFontsResource != 0) { - data.info.getResources().preloadFonts(preloadedFontsResource); + if (!Process.isIsolated()) { + try { + final ApplicationInfo info = + getPackageManager().getApplicationInfo( + data.appInfo.packageName, + PackageManager.GET_META_DATA /*flags*/, + UserHandle.myUserId()); + if (info.metaData != null) { + final int preloadedFontsResource = info.metaData.getInt( + ApplicationInfo.METADATA_PRELOADED_FONTS, 0); + if (preloadedFontsResource != 0) { + data.info.getResources().preloadFonts(preloadedFontsResource); + } } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); } } diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 4c9fb74c9c80..1026550b9b6b 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -543,7 +543,6 @@ public class AppOpsManager { OP_CAMERA, // Body sensors OP_BODY_SENSORS, - OP_REQUEST_DELETE_PACKAGES, // APPOP PERMISSIONS OP_ACCESS_NOTIFICATIONS, diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java index 5b61fdf8677f..310965e475b8 100644 --- a/core/java/android/app/ClientTransactionHandler.java +++ b/core/java/android/app/ClientTransactionHandler.java @@ -21,6 +21,7 @@ import android.content.pm.ApplicationInfo; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.os.IBinder; +import android.util.MergedConfiguration; import com.android.internal.content.ReferrerIntent; @@ -60,7 +61,7 @@ public abstract class ClientTransactionHandler { /** Destroy the activity. */ public abstract void handleDestroyActivity(IBinder token, boolean finishing, int configChanges, - boolean getNonConfigInstance); + boolean getNonConfigInstance, String reason); /** Pause the activity. */ public abstract void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving, @@ -124,6 +125,39 @@ public abstract class ClientTransactionHandler { public abstract ActivityThread.ActivityClientRecord getActivityClient(IBinder token); /** + * Prepare activity relaunch to update internal bookkeeping. This is used to track multiple + * relaunch and config update requests. + * @param token Activity token. + * @param pendingResults Activity results to be delivered. + * @param pendingNewIntents New intent messages to be delivered. + * @param configChanges Mask of configuration changes that have occurred. + * @param config New configuration applied to the activity. + * @param preserveWindow Whether the activity should try to reuse the window it created, + * including the decor view after the relaunch. + * @return An initialized instance of {@link ActivityThread.ActivityClientRecord} to use during + * relaunch, or {@code null} if relaunch cancelled. + */ + public abstract ActivityThread.ActivityClientRecord prepareRelaunchActivity(IBinder token, + List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, + int configChanges, MergedConfiguration config, boolean preserveWindow); + + /** + * Perform activity relaunch. + * @param r Activity client record prepared for relaunch. + * @param pendingActions Pending actions to be used on later stages of activity transaction. + * */ + public abstract void handleRelaunchActivity(ActivityThread.ActivityClientRecord r, + PendingTransactionActions pendingActions); + + /** + * Report that relaunch request was handled. + * @param token Target activity token. + * @param pendingActions Pending actions initialized on earlier stages of activity transaction. + * Used to check if we should report relaunch to WM. + * */ + public abstract void reportRelaunch(IBinder token, PendingTransactionActions pendingActions); + + /** * Debugging output. * @param pw {@link PrintWriter} to write logs to. * @param prefix Prefix to prepend to output. diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 4a9b2bcb16ed..99fb465f5c41 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -18,6 +18,7 @@ package android.app; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.TestApi; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentProvider; @@ -409,6 +410,7 @@ class ContextImpl extends Context { return sp; } + @GuardedBy("ContextImpl.class") private ArrayMap<File, SharedPreferencesImpl> getSharedPreferencesCacheLocked() { if (sSharedPrefsCache == null) { sSharedPrefsCache = new ArrayMap<>(); @@ -2263,6 +2265,7 @@ class ContextImpl extends Context { } /** @hide */ + @TestApi @Override public void setAutofillCompatibilityEnabled(boolean autofillCompatEnabled) { mIsAutofillCompatEnabled = autofillCompatEnabled; diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index 2b648ea6937d..eb260265c15f 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -609,18 +609,19 @@ public class Dialog implements DialogInterface, Window.Callback, /** * A key was pressed down. + * <p> + * If the focused view didn't want this event, this method is called. + * <p> + * Default implementation consumes {@link KeyEvent#KEYCODE_BACK KEYCODE_BACK} + * and, as of {@link android.os.Build.VERSION_CODES#P P}, {@link KeyEvent#KEYCODE_ESCAPE + * KEYCODE_ESCAPE} to later handle them in {@link #onKeyUp}. * - * <p>If the focused view didn't want this event, this method is called. - * - * <p>The default implementation consumed the KEYCODE_BACK to later - * handle it in {@link #onKeyUp}. - * * @see #onKeyUp * @see android.view.KeyEvent */ @Override public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_BACK) { + if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE) { event.startTracking(); return true; } @@ -640,16 +641,18 @@ public class Dialog implements DialogInterface, Window.Callback, /** * A key was released. - * - * <p>The default implementation handles KEYCODE_BACK to close the - * dialog. + * <p> + * Default implementation consumes {@link KeyEvent#KEYCODE_BACK KEYCODE_BACK} + * and, as of {@link android.os.Build.VERSION_CODES#P P}, {@link KeyEvent#KEYCODE_ESCAPE + * KEYCODE_ESCAPE} to close the dialog. * * @see #onKeyDown - * @see KeyEvent + * @see android.view.KeyEvent */ @Override public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking() + if ((keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE) + && event.isTracking() && !event.isCanceled()) { onBackPressed(); return true; diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 02be00268a45..54fd0c45a811 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -68,6 +68,7 @@ import android.os.WorkSource; import android.service.voice.IVoiceInteractionSession; import android.view.IRecentsAnimationRunner; import android.view.RemoteAnimationDefinition; +import android.view.RemoteAnimationAdapter; import com.android.internal.app.IVoiceInteractor; import com.android.internal.os.IResultReceiver; import com.android.internal.policy.IKeyguardDismissCallback; @@ -423,6 +424,8 @@ interface IActivityManager { void restart(); void performIdleMaintenance(); void takePersistableUriPermission(in Uri uri, int modeFlags, int userId); + boolean updatePersistableUriPermission(in Uri uri, boolean prefix, String packageName, + boolean grant, int userId); void releasePersistableUriPermission(in Uri uri, int modeFlags, int userId); ParceledListSlice getPersistedUriPermissions(in String packageName, boolean incoming); void appNotRespondingViaProvider(in IBinder connection); @@ -683,17 +686,24 @@ interface IActivityManager { // If a transaction which will also be used on the native side is being inserted, add it // alongside with other transactions of this kind at the top of this file. - void setShowWhenLocked(in IBinder token, boolean showWhenLocked); - void setTurnScreenOn(in IBinder token, boolean turnScreenOn); + void setShowWhenLocked(in IBinder token, boolean showWhenLocked); + void setTurnScreenOn(in IBinder token, boolean turnScreenOn); - /** - * Similar to {@link #startUserInBackground(int userId), but with a listener to report - * user unlock progress. - */ - boolean startUserInBackgroundWithListener(int userid, IProgressListener unlockProgressListener); + /** + * Similar to {@link #startUserInBackground(int userId), but with a listener to report + * user unlock progress. + */ + boolean startUserInBackgroundWithListener(int userid, IProgressListener unlockProgressListener); - /** - * Registers remote animations for a specific activity. - */ - void registerRemoteAnimations(in IBinder token, in RemoteAnimationDefinition definition); + /** + * Registers remote animations for a specific activity. + */ + void registerRemoteAnimations(in IBinder token, in RemoteAnimationDefinition definition); + + /** + * Registers a remote animation to be run for all activity starts from a certain package during + * a short predefined amount of time. + */ + void registerRemoteAnimationForNextActivityStart(in String packageName, + in RemoteAnimationAdapter adapter); } diff --git a/core/java/android/app/IAlarmManager.aidl b/core/java/android/app/IAlarmManager.aidl index 7b05b4918103..ded4c4954956 100644 --- a/core/java/android/app/IAlarmManager.aidl +++ b/core/java/android/app/IAlarmManager.aidl @@ -37,4 +37,5 @@ interface IAlarmManager { void remove(in PendingIntent operation, in IAlarmListener listener); long getNextWakeFromIdleTime(); AlarmManager.AlarmClockInfo getNextAlarmClock(int userId); + long currentNetworkTimeMillis(); } diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl index 9e99a78ccfbd..ae9b83ec0122 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -83,9 +83,6 @@ oneway interface IApplicationThread { int resultCode, in String data, in Bundle extras, boolean ordered, boolean sticky, int sendingUser, int processState); void scheduleLowMemory(); - void scheduleRelaunchActivity(IBinder token, in List<ResultInfo> pendingResults, - in List<ReferrerIntent> pendingNewIntents, int configChanges, boolean notResumed, - in Configuration config, in Configuration overrideConfig, boolean preserveWindow); void scheduleSleeping(IBinder token, boolean sleeping); void profilerControl(boolean start, in ProfilerInfo profilerInfo, int profileType); void setSchedulingGroup(int group); diff --git a/core/java/android/app/LocalActivityManager.java b/core/java/android/app/LocalActivityManager.java index 1d3459534f0c..e297719f9e4c 100644 --- a/core/java/android/app/LocalActivityManager.java +++ b/core/java/android/app/LocalActivityManager.java @@ -380,7 +380,7 @@ public class LocalActivityManager { } if (localLOGV) Log.v(TAG, r.id + ": destroying"); mActivityThread.performDestroyActivity(r, finish, 0 /* configChanges */, - false /* getNonConfigInstance */); + false /* getNonConfigInstance */, "LocalActivityManager::performDestroy"); r.activity = null; r.window = null; if (finish) { @@ -645,7 +645,7 @@ public class LocalActivityManager { LocalActivityRecord r = mActivityArray.get(i); if (localLOGV) Log.v(TAG, r.id + ": destroying"); mActivityThread.performDestroyActivity(r, finishing, 0 /* configChanges */, - false /* getNonConfigInstance */); + false /* getNonConfigInstance */, "LocalActivityManager::dispatchDestroy"); } mActivities.clear(); mActivityArray.clear(); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 6e4098646b27..e80610b0fa83 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1055,11 +1055,10 @@ public class Notification implements Parcelable /** * {@link #extras} key: A * {@link android.content.ContentUris content URI} pointing to an image that can be displayed - * in the background when the notification is selected. The URI must point to an image stream - * suitable for passing into + * in the background when the notification is selected. Used on television platforms. + * The URI must point to an image stream suitable for passing into * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream) - * BitmapFactory.decodeStream}; all other content types will be ignored. The content provider - * URI used for this purpose must require no permissions to read the image data. + * BitmapFactory.decodeStream}; all other content types will be ignored. */ public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri"; @@ -4617,10 +4616,15 @@ public class Notification implements Parcelable if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS; for (int i=0; i<N; i++) { Action action = mActions.get(i); - validRemoteInput |= hasValidRemoteInput(action); + boolean actionHasValidInput = hasValidRemoteInput(action); + validRemoteInput |= actionHasValidInput; final RemoteViews button = generateActionButton(action, emphazisedMode, i % 2 != 0, p.ambient); + if (actionHasValidInput) { + // Clear the drawable + button.setInt(R.id.action0, "setBackgroundResource", 0); + } big.addView(R.id.actions, button); } } else { @@ -6435,7 +6439,7 @@ public class Notification implements Parcelable public RemoteViews makeContentView(boolean increasedHeight) { mBuilder.mOriginalActions = mBuilder.mActions; mBuilder.mActions = new ArrayList<>(); - RemoteViews remoteViews = makeBigContentView(true /* showRightIcon */); + RemoteViews remoteViews = makeMessagingView(true /* isCollapsed */); mBuilder.mActions = mBuilder.mOriginalActions; mBuilder.mOriginalActions = null; return remoteViews; @@ -6470,11 +6474,11 @@ public class Notification implements Parcelable */ @Override public RemoteViews makeBigContentView() { - return makeBigContentView(false /* showRightIcon */); + return makeMessagingView(false /* isCollapsed */); } @NonNull - private RemoteViews makeBigContentView(boolean showRightIcon) { + private RemoteViews makeMessagingView(boolean isCollapsed) { CharSequence conversationTitle = !TextUtils.isEmpty(super.mBigContentTitle) ? super.mBigContentTitle : mConversationTitle; @@ -6485,21 +6489,24 @@ public class Notification implements Parcelable nameReplacement = conversationTitle; conversationTitle = null; } + boolean hideLargeIcon = !isCollapsed || isOneToOne; RemoteViews contentView = mBuilder.applyStandardTemplateWithActions( mBuilder.getMessagingLayoutResource(), mBuilder.mParams.reset().hasProgress(false).title(conversationTitle).text(null) - .hideLargeIcon(!showRightIcon || isOneToOne) + .hideLargeIcon(hideLargeIcon) .headerTextSecondary(conversationTitle) - .alwaysShowReply(showRightIcon)); + .alwaysShowReply(isCollapsed)); addExtras(mBuilder.mN.extras); // also update the end margin if there is an image int endMargin = R.dimen.notification_content_margin_end; - if (mBuilder.mN.hasLargeIcon() && showRightIcon) { + if (isCollapsed) { endMargin = R.dimen.notification_content_plus_picture_margin_end; } contentView.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin); contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor", mBuilder.resolveContrastColor()); + contentView.setBoolean(R.id.status_bar_latest_event_content, "setIsCollapsed", + isCollapsed); contentView.setIcon(R.id.status_bar_latest_event_content, "setLargeIcon", mBuilder.mN.mLargeIcon); contentView.setCharSequence(R.id.status_bar_latest_event_content, "setNameReplacement", @@ -6566,7 +6573,7 @@ public class Notification implements Parcelable */ @Override public RemoteViews makeHeadsUpContentView(boolean increasedHeight) { - RemoteViews remoteViews = makeBigContentView(true /* showRightIcon */); + RemoteViews remoteViews = makeMessagingView(true /* isCollapsed */); remoteViews.setInt(R.id.notification_messaging, "setMaxDisplayedLines", 1); return remoteViews; } @@ -8855,6 +8862,7 @@ public class Notification implements Parcelable private static final String EXTRA_CONTENT_INTENT = "content_intent"; private static final String EXTRA_DELETE_INTENT = "delete_intent"; private static final String EXTRA_CHANNEL_ID = "channel_id"; + private static final String EXTRA_SUPPRESS_SHOW_OVER_APPS = "suppressShowOverApps"; // Flags bitwise-ored to mFlags private static final int FLAG_AVAILABLE_ON_TV = 0x1; @@ -8863,6 +8871,7 @@ public class Notification implements Parcelable private String mChannelId; private PendingIntent mContentIntent; private PendingIntent mDeleteIntent; + private boolean mSuppressShowOverApps; /** * Create a {@link TvExtender} with default options. @@ -8882,6 +8891,7 @@ public class Notification implements Parcelable if (bundle != null) { mFlags = bundle.getInt(EXTRA_FLAGS); mChannelId = bundle.getString(EXTRA_CHANNEL_ID); + mSuppressShowOverApps = bundle.getBoolean(EXTRA_SUPPRESS_SHOW_OVER_APPS); mContentIntent = bundle.getParcelable(EXTRA_CONTENT_INTENT); mDeleteIntent = bundle.getParcelable(EXTRA_DELETE_INTENT); } @@ -8898,6 +8908,7 @@ public class Notification implements Parcelable bundle.putInt(EXTRA_FLAGS, mFlags); bundle.putString(EXTRA_CHANNEL_ID, mChannelId); + bundle.putBoolean(EXTRA_SUPPRESS_SHOW_OVER_APPS, mSuppressShowOverApps); if (mContentIntent != null) { bundle.putParcelable(EXTRA_CONTENT_INTENT, mContentIntent); } @@ -8990,6 +9001,23 @@ public class Notification implements Parcelable public PendingIntent getDeleteIntent() { return mDeleteIntent; } + + /** + * Specifies whether this notification should suppress showing a message over top of apps + * outside of the launcher. + */ + public TvExtender setSuppressShowOverApps(boolean suppress) { + mSuppressShowOverApps = suppress; + return this; + } + + /** + * Returns true if this notification should not show messages over top of apps + * outside of the launcher. + */ + public boolean getSuppressShowOverApps() { + return mSuppressShowOverApps; + } } /** diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 538623f26a95..b10e60870721 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -24,6 +24,7 @@ import android.annotation.TestApi; import android.app.Notification.Builder; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.content.pm.ParceledListSlice; import android.graphics.drawable.Icon; import android.net.Uri; @@ -350,6 +351,14 @@ public class NotificationManager { * the same tag and id has already been posted by your application and has not yet been * canceled, it will be replaced by the updated information. * + * All {@link android.service.notification.NotificationListenerService listener services} will + * be granted {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} access to any {@link Uri uris} + * provided on this notification or the + * {@link NotificationChannel} this notification is posted to using + * {@link Context#grantUriPermission(String, Uri, int)}. Permission will be revoked when the + * notification is canceled, or you can revoke permissions with + * {@link Context#revokeUriPermission(Uri, int)}. + * * @param tag A string identifier for this notification. May be {@code null}. * @param id An identifier for this notification. The pair (tag, id) must be unique * within your application. @@ -370,11 +379,13 @@ public class NotificationManager { String pkg = mContext.getPackageName(); // Fix the notification as best we can. Notification.addFieldsFromContext(mContext, notification); + if (notification.sound != null) { notification.sound = notification.sound.getCanonicalUri(); if (StrictMode.vmFileUriExposureEnabled()) { notification.sound.checkFileUriExposed("Notification.sound"); } + } fixLegacySmallIcon(notification, pkg); if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) { @@ -385,6 +396,7 @@ public class NotificationManager { } if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")"); notification.reduceImageSizes(mContext); + ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); boolean isLowRam = am.isLowRamDevice(); final Notification copy = Builder.maybeCloneStrippedForDelivery(notification, isLowRam); diff --git a/core/java/android/app/ProcessMemoryState.java b/core/java/android/app/ProcessMemoryState.java new file mode 100644 index 000000000000..39db16d10575 --- /dev/null +++ b/core/java/android/app/ProcessMemoryState.java @@ -0,0 +1,88 @@ +/* + * 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.app; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * The memory stats for a process. + * {@hide} + */ +public class ProcessMemoryState implements Parcelable { + public int uid; + public String processName; + public int oomScore; + public long pgfault; + public long pgmajfault; + public long rssInBytes; + public long cacheInBytes; + public long swapInBytes; + + public ProcessMemoryState(int uid, String processName, int oomScore, long pgfault, + long pgmajfault, long rssInBytes, long cacheInBytes, + long swapInBytes) { + this.uid = uid; + this.processName = processName; + this.oomScore = oomScore; + this.pgfault = pgfault; + this.pgmajfault = pgmajfault; + this.rssInBytes = rssInBytes; + this.cacheInBytes = cacheInBytes; + this.swapInBytes = swapInBytes; + } + + private ProcessMemoryState(Parcel in) { + uid = in.readInt(); + processName = in.readString(); + oomScore = in.readInt(); + pgfault = in.readLong(); + pgmajfault = in.readLong(); + rssInBytes = in.readLong(); + cacheInBytes = in.readLong(); + swapInBytes = in.readLong(); + } + + public static final Creator<ProcessMemoryState> CREATOR = new Creator<ProcessMemoryState>() { + @Override + public ProcessMemoryState createFromParcel(Parcel in) { + return new ProcessMemoryState(in); + } + + @Override + public ProcessMemoryState[] newArray(int size) { + return new ProcessMemoryState[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int i) { + parcel.writeInt(uid); + parcel.writeString(processName); + parcel.writeInt(oomScore); + parcel.writeLong(pgfault); + parcel.writeLong(pgmajfault); + parcel.writeLong(rssInBytes); + parcel.writeLong(cacheInBytes); + parcel.writeLong(swapInBytes); + } +} diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java index c525c89c88e7..c2c91c2bcbd6 100644 --- a/core/java/android/app/StatsManager.java +++ b/core/java/android/app/StatsManager.java @@ -30,20 +30,27 @@ import android.util.Slog; * @hide */ @SystemApi -public final class StatsManager extends android.util.StatsManager { // TODO: Remove the extends. +public final class StatsManager { IStatsManager mService; private static final String TAG = "StatsManager"; + private static final boolean DEBUG = false; - /** Long extra of uid that added the relevant stats config. */ - public static final String EXTRA_STATS_CONFIG_UID = - "android.app.extra.STATS_CONFIG_UID"; - /** Long extra of the relevant stats config's configKey. */ - public static final String EXTRA_STATS_CONFIG_KEY = - "android.app.extra.STATS_CONFIG_KEY"; - /** Long extra of the relevant statsd_config.proto's Subscription.id. */ + /** + * Long extra of uid that added the relevant stats config. + */ + public static final String EXTRA_STATS_CONFIG_UID = "android.app.extra.STATS_CONFIG_UID"; + /** + * Long extra of the relevant stats config's configKey. + */ + public static final String EXTRA_STATS_CONFIG_KEY = "android.app.extra.STATS_CONFIG_KEY"; + /** + * Long extra of the relevant statsd_config.proto's Subscription.id. + */ public static final String EXTRA_STATS_SUBSCRIPTION_ID = "android.app.extra.STATS_SUBSCRIPTION_ID"; - /** Long extra of the relevant statsd_config.proto's Subscription.rule_id. */ + /** + * Long extra of the relevant statsd_config.proto's Subscription.rule_id. + */ public static final String EXTRA_STATS_SUBSCRIPTION_RULE_ID = "android.app.extra.STATS_SUBSCRIPTION_RULE_ID"; /** @@ -68,28 +75,34 @@ public final class StatsManager extends android.util.StatsManager { // TODO: Rem } /** + * Temporary. Will be deleted. + */ + @RequiresPermission(Manifest.permission.DUMP) + public boolean addConfiguration(long configKey, byte[] config, String a, String b) { + return addConfiguration(configKey, config); + } + + /** * Clients can send a configuration and simultaneously registers the name of a broadcast * receiver that listens for when it should request data. * * @param configKey An arbitrary integer that allows clients to track the configuration. * @param config Wire-encoded StatsDConfig proto that specifies metrics (and all * dependencies eg, conditions and matchers). - * @param pkg The package name to receive the broadcast. - * @param cls The name of the class that receives the broadcast. * @return true if successful */ @RequiresPermission(Manifest.permission.DUMP) - public boolean addConfiguration(long configKey, byte[] config, String pkg, String cls) { + public boolean addConfiguration(long configKey, byte[] config) { synchronized (this) { try { IStatsManager service = getIStatsManagerLocked(); if (service == null) { - Slog.d(TAG, "Failed to find statsd when adding configuration"); + if (DEBUG) Slog.d(TAG, "Failed to find statsd when adding configuration"); return false; } - return service.addConfiguration(configKey, config, pkg, cls); + return service.addConfiguration(configKey, config); } catch (RemoteException e) { - Slog.d(TAG, "Failed to connect to statsd when adding configuration"); + if (DEBUG) Slog.d(TAG, "Failed to connect to statsd when adding configuration"); return false; } } @@ -107,12 +120,12 @@ public final class StatsManager extends android.util.StatsManager { // TODO: Rem try { IStatsManager service = getIStatsManagerLocked(); if (service == null) { - Slog.d(TAG, "Failed to find statsd when removing configuration"); + if (DEBUG) Slog.d(TAG, "Failed to find statsd when removing configuration"); return false; } return service.removeConfiguration(configKey); } catch (RemoteException e) { - Slog.d(TAG, "Failed to connect to statsd when removing configuration"); + if (DEBUG) Slog.d(TAG, "Failed to connect to statsd when removing configuration"); return false; } } @@ -121,38 +134,34 @@ public final class StatsManager extends android.util.StatsManager { // TODO: Rem /** * Set the PendingIntent to be used when broadcasting subscriber information to the given * subscriberId within the given config. - * * <p> * Suppose that the calling uid has added a config with key configKey, and that in this config * it is specified that when a particular anomaly is detected, a broadcast should be sent to * a BroadcastSubscriber with id subscriberId. This function links the given pendingIntent with * that subscriberId (for that config), so that this pendingIntent is used to send the broadcast * when the anomaly is detected. - * * <p> * When statsd sends the broadcast, the PendingIntent will used to send an intent with * information of - * {@link #EXTRA_STATS_CONFIG_UID}, - * {@link #EXTRA_STATS_CONFIG_KEY}, - * {@link #EXTRA_STATS_SUBSCRIPTION_ID}, - * {@link #EXTRA_STATS_SUBSCRIPTION_RULE_ID}, and - * {@link #EXTRA_STATS_DIMENSIONS_VALUE}. - * + * {@link #EXTRA_STATS_CONFIG_UID}, + * {@link #EXTRA_STATS_CONFIG_KEY}, + * {@link #EXTRA_STATS_SUBSCRIPTION_ID}, + * {@link #EXTRA_STATS_SUBSCRIPTION_RULE_ID}, and + * {@link #EXTRA_STATS_DIMENSIONS_VALUE}. * <p> * This function can only be called by the owner (uid) of the config. It must be called each * time statsd starts. The config must have been added first (via addConfiguration()). * - * @param configKey The integer naming the config to which this subscriber is attached. - * @param subscriberId ID of the subscriber, as used in the config. + * @param configKey The integer naming the config to which this subscriber is attached. + * @param subscriberId ID of the subscriber, as used in the config. * @param pendingIntent the PendingIntent to use when broadcasting info to the subscriber * associated with the given subscriberId. May be null, in which case * it undoes any previous setting of this subscriberId. * @return true if successful */ @RequiresPermission(Manifest.permission.DUMP) - public boolean setBroadcastSubscriber(long configKey, - long subscriberId, - PendingIntent pendingIntent) { + public boolean setBroadcastSubscriber( + long configKey, long subscriberId, PendingIntent pendingIntent) { synchronized (this) { try { IStatsManager service = getIStatsManagerLocked(); @@ -175,6 +184,44 @@ public final class StatsManager extends android.util.StatsManager { // TODO: Rem } /** + * Registers the operation that is called to retrieve the metrics data. This must be called + * each time statsd starts. The config must have been added first (via addConfiguration(), + * although addConfiguration could have been called on a previous boot). This operation allows + * statsd to send metrics data whenever statsd determines that the metrics in memory are + * approaching the memory limits. The fetch operation should call {@link #getData} to fetch the + * data, which also deletes the retrieved metrics from statsd's memory. + * + * @param configKey The integer naming the config to which this operation is attached. + * @param pendingIntent the PendingIntent to use when broadcasting info to the subscriber + * associated with the given subscriberId. May be null, in which case + * it removes any associated pending intent with this configKey. + * @return true if successful + */ + @RequiresPermission(Manifest.permission.DUMP) + public boolean setDataFetchOperation(long configKey, PendingIntent pendingIntent) { + synchronized (this) { + try { + IStatsManager service = getIStatsManagerLocked(); + if (service == null) { + Slog.d(TAG, "Failed to find statsd when registering data listener."); + return false; + } + if (pendingIntent == null) { + return service.removeDataFetchOperation(configKey); + } else { + // Extracts IIntentSender from the PendingIntent and turns it into an IBinder. + IBinder intentSender = pendingIntent.getTarget().asBinder(); + return service.setDataFetchOperation(configKey, intentSender); + } + + } catch (RemoteException e) { + Slog.d(TAG, "Failed to connect to statsd when registering data listener."); + return false; + } + } + } + + /** * Clients can request data with a binder call. This getter is destructive and also clears * the retrieved metrics from statsd memory. * @@ -187,12 +234,12 @@ public final class StatsManager extends android.util.StatsManager { // TODO: Rem try { IStatsManager service = getIStatsManagerLocked(); if (service == null) { - Slog.d(TAG, "Failed to find statsd when getting data"); + if (DEBUG) Slog.d(TAG, "Failed to find statsd when getting data"); return null; } return service.getData(configKey); } catch (RemoteException e) { - Slog.d(TAG, "Failed to connecto statsd when getting data"); + if (DEBUG) Slog.d(TAG, "Failed to connecto statsd when getting data"); return null; } } @@ -211,12 +258,12 @@ public final class StatsManager extends android.util.StatsManager { // TODO: Rem try { IStatsManager service = getIStatsManagerLocked(); if (service == null) { - Slog.d(TAG, "Failed to find statsd when getting metadata"); + if (DEBUG) Slog.d(TAG, "Failed to find statsd when getting metadata"); return null; } return service.getMetadata(); } catch (RemoteException e) { - Slog.d(TAG, "Failed to connecto statsd when getting metadata"); + if (DEBUG) Slog.d(TAG, "Failed to connecto statsd when getting metadata"); return null; } } diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 87f32b2613bc..aa52cdef70c6 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -38,7 +38,6 @@ import android.content.ClipboardManager; import android.content.Context; import android.content.IRestrictionsManager; import android.content.RestrictionsManager; -import android.content.pm.ApplicationInfo; import android.content.pm.CrossProfileApps; import android.content.pm.ICrossProfileApps; import android.content.pm.IShortcutService; @@ -90,7 +89,6 @@ import android.net.lowpan.ILowpanManager; import android.net.lowpan.LowpanManager; import android.net.nsd.INsdManager; import android.net.nsd.NsdManager; -import android.net.wifi.IRttManager; import android.net.wifi.IWifiManager; import android.net.wifi.IWifiScanner; import android.net.wifi.RttManager; @@ -117,7 +115,6 @@ import android.os.ISystemUpdateManager; import android.os.IUserManager; import android.os.IncidentManager; import android.os.PowerManager; -import android.os.Process; import android.os.RecoverySystem; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; @@ -639,13 +636,13 @@ final class SystemServiceRegistry { registerService(Context.WIFI_RTT_SERVICE, RttManager.class, new CachedServiceFetcher<RttManager>() { - @Override - public RttManager createService(ContextImpl ctx) throws ServiceNotFoundException { - IBinder b = ServiceManager.getServiceOrThrow(Context.WIFI_RTT_SERVICE); - IRttManager service = IRttManager.Stub.asInterface(b); - return new RttManager(ctx.getOuterContext(), service, - ConnectivityThread.getInstanceLooper()); - }}); + @Override + public RttManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.WIFI_RTT_RANGING_SERVICE); + IWifiRttManager service = IWifiRttManager.Stub.asInterface(b); + return new RttManager(ctx.getOuterContext(), + new WifiRttManager(ctx.getOuterContext(), service)); + }}); registerService(Context.WIFI_RTT_RANGING_SERVICE, WifiRttManager.class, new CachedServiceFetcher<WifiRttManager>() { diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java index 28e845a04e44..8c30fc403713 100644 --- a/core/java/android/app/admin/DeviceAdminReceiver.java +++ b/core/java/android/app/admin/DeviceAdminReceiver.java @@ -19,6 +19,8 @@ package android.app.admin; import android.accounts.AccountManager; import android.annotation.BroadcastBehavior; import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; @@ -34,9 +36,6 @@ import android.os.Process; import android.os.UserHandle; import android.security.KeyChain; -import libcore.util.NonNull; -import libcore.util.Nullable; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -928,29 +927,29 @@ public class DeviceAdminReceiver extends BroadcastReceiver { int networkLogsCount) { } - /** - * Called when a user or profile is created. - * - * <p>This callback is only applicable to device owners. - * - * @param context The running context as per {@link #onReceive}. - * @param intent The received intent as per {@link #onReceive}. - * @param newUser The {@link UserHandle} of the user that has just been added. - */ - public void onUserAdded(Context context, Intent intent, UserHandle newUser) { - } - - /** - * Called when a user or profile is removed. - * - * <p>This callback is only applicable to device owners. - * - * @param context The running context as per {@link #onReceive}. - * @param intent The received intent as per {@link #onReceive}. - * @param removedUser The {@link UserHandle} of the user that has just been removed. - */ - public void onUserRemoved(Context context, Intent intent, UserHandle removedUser) { - } + /** + * Called when a user or profile is created. + * + * <p>This callback is only applicable to device owners. + * + * @param context The running context as per {@link #onReceive}. + * @param intent The received intent as per {@link #onReceive}. + * @param newUser The {@link UserHandle} of the user that has just been added. + */ + public void onUserAdded(Context context, Intent intent, @NonNull UserHandle newUser) { + } + + /** + * Called when a user or profile is removed. + * + * <p>This callback is only applicable to device owners. + * + * @param context The running context as per {@link #onReceive}. + * @param intent The received intent as per {@link #onReceive}. + * @param removedUser The {@link UserHandle} of the user that has just been removed. + */ + public void onUserRemoved(Context context, Intent intent, @NonNull UserHandle removedUser) { + } /** * Called when a user or profile is started. @@ -961,7 +960,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver { * @param intent The received intent as per {@link #onReceive}. * @param startedUser The {@link UserHandle} of the user that has just been started. */ - public void onUserStarted(Context context, Intent intent, UserHandle startedUser) { + public void onUserStarted(Context context, Intent intent, @NonNull UserHandle startedUser) { } /** @@ -973,7 +972,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver { * @param intent The received intent as per {@link #onReceive}. * @param stoppedUser The {@link UserHandle} of the user that has just been stopped. */ - public void onUserStopped(Context context, Intent intent, UserHandle stoppedUser) { + public void onUserStopped(Context context, Intent intent, @NonNull UserHandle stoppedUser) { } /** @@ -985,7 +984,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver { * @param intent The received intent as per {@link #onReceive}. * @param switchedUser The {@link UserHandle} of the user that has just been switched to. */ - public void onUserSwitched(Context context, Intent intent, UserHandle switchedUser) { + public void onUserSwitched(Context context, Intent intent, @NonNull UserHandle switchedUser) { } /** diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 8d161006be76..14b2119b4e4b 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -21,6 +21,7 @@ import android.annotation.ColorInt; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresFeature; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -116,6 +117,7 @@ import java.util.concurrent.Executor; * guide. </div> */ @SystemService(Context.DEVICE_POLICY_SERVICE) +@RequiresFeature(PackageManager.FEATURE_DEVICE_ADMIN) public class DevicePolicyManager { private static String TAG = "DevicePolicyManager"; @@ -3444,9 +3446,6 @@ public class DevicePolicyManager { /** * Flag for {@link #wipeData(int)}: also erase the device's eUICC data. - * - * TODO(b/35851809): make this public. - * @hide */ public static final int WIPE_EUICC = 0x0004; @@ -9404,41 +9403,6 @@ public class DevicePolicyManager { } /** - * Allows/disallows printing. - * - * Called by a device owner or a profile owner. - * Device owner changes policy for all users. Profile owner can override it if present. - * Printing is enabled by default. If {@code FEATURE_PRINTING} is absent, the call is ignored. - * - * @param admin which {@link DeviceAdminReceiver} this request is associated with. - * @param enabled whether printing should be allowed or not. - * @throws SecurityException if {@code admin} is neither device, nor profile owner. - */ - public void setPrintingEnabled(@NonNull ComponentName admin, boolean enabled) { - try { - mService.setPrintingEnabled(admin, enabled); - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); - } - } - - /** - * Returns whether printing is enabled for this user. - * - * Always {@code false} if {@code FEATURE_PRINTING} is absent. - * Otherwise, {@code true} by default. - * - * @return {@code true} iff printing is enabled. - */ - public boolean isPrintingEnabled() { - try { - return mService.isPrintingEnabled(); - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); - } - } - - /** * Called by device owner to add an override APN. * * @param admin which {@link DeviceAdminReceiver} this request is associated with diff --git a/core/java/android/app/admin/FreezeInterval.java b/core/java/android/app/admin/FreezeInterval.java index 7acdfc8fe100..de5e21ac75c4 100644 --- a/core/java/android/app/admin/FreezeInterval.java +++ b/core/java/android/app/admin/FreezeInterval.java @@ -84,6 +84,10 @@ public class FreezeInterval { } } + boolean after(LocalDate localDate) { + return mStartDay > dayOfYearDisregardLeapYear(localDate); + } + /** * Instantiate the current interval to real calendar dates, given a calendar date * {@code now}. If the interval contains now, the returned calendar dates should be the @@ -161,7 +165,7 @@ public class FreezeInterval { * 3. At most one wrapped Interval remains, and it will be at the end of the list * @hide */ - private static List<FreezeInterval> canonicalizeIntervals(List<FreezeInterval> intervals) { + protected static List<FreezeInterval> canonicalizeIntervals(List<FreezeInterval> intervals) { boolean[] taken = new boolean[DAYS_IN_YEAR]; // First convert the intervals into flat array for (FreezeInterval interval : intervals) { diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index ef990071dbfd..5218a7340ec9 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -402,9 +402,6 @@ interface IDevicePolicyManager { CharSequence getStartUserSessionMessage(in ComponentName admin); CharSequence getEndUserSessionMessage(in ComponentName admin); - void setPrintingEnabled(in ComponentName admin, boolean enabled); - boolean isPrintingEnabled(); - List<String> setMeteredDataDisabled(in ComponentName admin, in List<String> packageNames); List<String> getMeteredDataDisabled(in ComponentName admin); diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java index 08effd9c148a..faaa0043cce8 100644 --- a/core/java/android/app/admin/SecurityLog.java +++ b/core/java/android/app/admin/SecurityLog.java @@ -77,6 +77,9 @@ public class SecurityLog { TAG_KEY_DESTRUCTION, TAG_CERT_AUTHORITY_INSTALLED, TAG_CERT_AUTHORITY_REMOVED, + TAG_CRYPTO_SELF_TEST_COMPLETED, + TAG_KEY_INTEGRITY_VIOLATION, + TAG_CERT_VALIDATION_FAILURE, }) public @interface SecurityLogTag {} @@ -316,6 +319,7 @@ public class SecurityLog { * {@link SecurityEvent#getData()}: * <li> [0] admin package name ({@code String}), * <li> [1] admin user ID ({@code Integer}). + * <li> [2] target user ID ({@code Integer}) */ public static final int TAG_REMOTE_LOCK = SecurityLogTags.SECURITY_REMOTE_LOCK; @@ -400,6 +404,31 @@ public class SecurityLog { SecurityLogTags.SECURITY_USER_RESTRICTION_REMOVED; /** + * Indicates that cryptographic functionality self test has completed. The log entry contains an + * {@code Integer} payload, indicating the result of the test (0 if the test failed, 1 if + * succeeded) and accessible via {@link SecurityEvent#getData()}. + */ + public static final int TAG_CRYPTO_SELF_TEST_COMPLETED = + SecurityLogTags.SECURITY_CRYPTO_SELF_TEST_COMPLETED; + + /** + * Indicates a failed cryptographic key integrity check. The log entry contains the following + * information about the event, encapsulated in an {@link Object} array and accessible via + * {@link SecurityEvent#getData()}: + * <li> [0] alias of the key ({@code String}) + * <li> [1] owner application uid ({@code Integer}). + */ + public static final int TAG_KEY_INTEGRITY_VIOLATION = + SecurityLogTags.SECURITY_KEY_INTEGRITY_VIOLATION; + + /** + * Indicates a failure to validate X.509v3 certificate. The log entry contains a {@code String} + * payload indicating the failure reason, accessible via {@link SecurityEvent#getData()}. + */ + public static final int TAG_CERT_VALIDATION_FAILURE = + SecurityLogTags.SECURITY_CERT_VALIDATION_FAILURE; + + /** * Event severity level indicating that the event corresponds to normal workflow. */ public static final int LEVEL_INFO = 1; @@ -529,6 +558,7 @@ public class SecurityLog { case TAG_USER_RESTRICTION_REMOVED: return LEVEL_INFO; case TAG_CERT_AUTHORITY_REMOVED: + case TAG_CRYPTO_SELF_TEST_COMPLETED: return getSuccess() ? LEVEL_INFO : LEVEL_ERROR; case TAG_CERT_AUTHORITY_INSTALLED: case TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT: @@ -538,7 +568,10 @@ public class SecurityLog { return getSuccess() ? LEVEL_INFO : LEVEL_WARNING; case TAG_LOG_BUFFER_SIZE_CRITICAL: case TAG_WIPE_FAILURE: + case TAG_KEY_INTEGRITY_VIOLATION: return LEVEL_ERROR; + case TAG_CERT_VALIDATION_FAILURE: + return LEVEL_WARNING; default: return LEVEL_INFO; } diff --git a/core/java/android/app/admin/SecurityLogTags.logtags b/core/java/android/app/admin/SecurityLogTags.logtags index be626786c3c6..fe2519d2bdf1 100644 --- a/core/java/android/app/admin/SecurityLogTags.logtags +++ b/core/java/android/app/admin/SecurityLogTags.logtags @@ -34,4 +34,7 @@ option java_package android.app.admin 210027 security_user_restriction_added (package|3),(admin_user|1),(restriction|3) 210028 security_user_restriction_removed (package|3),(admin_user|1),(restriction|3) 210029 security_cert_authority_installed (success|1),(subject|3) -210030 security_cert_authority_removed (success|1),(subject|3)
\ No newline at end of file +210030 security_cert_authority_removed (success|1),(subject|3) +210031 security_crypto_self_test_completed (success|1) +210032 security_key_integrity_violation (key_id|3),(uid|1) +210033 security_cert_validation_failure (reason|3) diff --git a/core/java/android/app/admin/SystemUpdatePolicy.java b/core/java/android/app/admin/SystemUpdatePolicy.java index 05d3fd9c632c..47b3a81d0174 100644 --- a/core/java/android/app/admin/SystemUpdatePolicy.java +++ b/core/java/android/app/admin/SystemUpdatePolicy.java @@ -21,6 +21,7 @@ import static org.xmlpull.v1.XmlPullParser.END_TAG; import static org.xmlpull.v1.XmlPullParser.TEXT; import android.annotation.IntDef; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; @@ -33,9 +34,15 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.time.Instant; import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; import java.util.ArrayList; +import java.util.Calendar; import java.util.List; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; /** @@ -103,6 +110,19 @@ public class SystemUpdatePolicy implements Parcelable { */ public static final int TYPE_POSTPONE = 3; + /** + * Incoming system updates (including security updates) should be blocked. This flag is not + * exposed to third-party apps (and any attempt to set it will raise exceptions). This is used + * to represent the current installation option type to the privileged system update clients, + * for example to indicate OTA freeze is currently in place or when system is outside a daily + * maintenance window. + * + * @see InstallationOption + * @hide + */ + @SystemApi + public static final int TYPE_PAUSE = 4; + private static final String KEY_POLICY_TYPE = "policy_type"; private static final String KEY_INSTALL_WINDOW_START = "install_window_start"; private static final String KEY_INSTALL_WINDOW_END = "install_window_end"; @@ -460,6 +480,30 @@ public class SystemUpdatePolicy implements Parcelable { return null; } + /** + * Returns time (in milliseconds) until the start of the next freeze period, assuming now + * is not within a freeze period. + */ + private long timeUntilNextFreezePeriod(long now) { + List<FreezeInterval> sortedPeriods = FreezeInterval.canonicalizeIntervals(mFreezePeriods); + LocalDate nowDate = millisToDate(now); + LocalDate nextFreezeStart = null; + for (FreezeInterval interval : sortedPeriods) { + if (interval.after(nowDate)) { + nextFreezeStart = interval.toCurrentOrFutureRealDates(nowDate).first; + break; + } else if (interval.contains(nowDate)) { + throw new IllegalArgumentException("Given date is inside a freeze period"); + } + } + if (nextFreezeStart == null) { + // If no interval is after now, then it must be the one that starts at the beginning + // of next year + nextFreezeStart = sortedPeriods.get(0).toCurrentOrFutureRealDates(nowDate).first; + } + return dateToMillis(nextFreezeStart) - now; + } + /** @hide */ public void validateFreezePeriods() { FreezeInterval.validatePeriods(mFreezePeriods); @@ -472,6 +516,134 @@ public class SystemUpdatePolicy implements Parcelable { prevPeriodEnd, now); } + /** + * An installation option represents how system update clients should act on incoming system + * updates and how long this action is valid for, given the current system update policy. Its + * action could be one of the following + * <ul> + * <li> {@code TYPE_INSTALL_AUTOMATIC} system updates should be installed immedately and without + * user intervention as soon as they become available. + * <li> {@code TYPE_POSTPONE} system updates should be postponed for a maximum of 30 days + * <li> {@code TYPE_PAUSE} system updates should be postponed indefinitely until further notice + * </ul> + * + * The effective time measures how long this installation option is valid for from the queried + * time, in milliseconds. + * + * This is an internal API for system update clients. + * @hide + */ + @SystemApi + public static class InstallationOption { + private final int mType; + private long mEffectiveTime; + + InstallationOption(int type, long effectiveTime) { + this.mType = type; + this.mEffectiveTime = effectiveTime; + } + + public int getType() { + return mType; + } + + public long getEffectiveTime() { + return mEffectiveTime; + } + + /** @hide */ + protected void limitEffectiveTime(long otherTime) { + mEffectiveTime = Long.min(mEffectiveTime, otherTime); + } + } + + /** + * Returns the installation option at the specified time, under the current + * {@code SystemUpdatePolicy} object. This is a convenience method for system update clients + * so they can instantiate this policy at any given time and find out what to do with incoming + * system updates, without the need of examining the overall policy structure. + * + * Normally the system update clients will query the current installation option by calling this + * method with the current timestamp, and act on the returned option until its effective time + * lapses. It can then query the latest option using a new timestamp. It should also listen + * for {@code DevicePolicyManager#ACTION_SYSTEM_UPDATE_POLICY_CHANGED} broadcast, in case the + * whole policy is updated. + * + * @param when At what time the intallation option is being queried, specified in number of + milliseonds since the epoch. + * @see InstallationOption + * @hide + */ + @SystemApi + public InstallationOption getInstallationOptionAt(long when) { + LocalDate whenDate = millisToDate(when); + Pair<LocalDate, LocalDate> current = getCurrentFreezePeriod(whenDate); + if (current != null) { + return new InstallationOption(TYPE_PAUSE, + dateToMillis(roundUpLeapDay(current.second).plusDays(1)) - when); + } + // We are not within a freeze period, query the underlying policy. + // But also consider the start of the next freeze period, which might + // reduce the effective time of the current installation option + InstallationOption option = getInstallationOptionRegardlessFreezeAt(when); + if (mFreezePeriods.size() > 0) { + option.limitEffectiveTime(timeUntilNextFreezePeriod(when)); + } + return option; + } + + private InstallationOption getInstallationOptionRegardlessFreezeAt(long when) { + if (mPolicyType == TYPE_INSTALL_AUTOMATIC || mPolicyType == TYPE_POSTPONE) { + return new InstallationOption(mPolicyType, Long.MAX_VALUE); + } else if (mPolicyType == TYPE_INSTALL_WINDOWED) { + Calendar query = Calendar.getInstance(); + query.setTimeInMillis(when); + // Calculate the number of milliseconds since midnight of the time specified by when + long whenMillis = TimeUnit.HOURS.toMillis(query.get(Calendar.HOUR_OF_DAY)) + + TimeUnit.MINUTES.toMillis(query.get(Calendar.MINUTE)) + + TimeUnit.SECONDS.toMillis(query.get(Calendar.SECOND)) + + query.get(Calendar.MILLISECOND); + long windowStartMillis = TimeUnit.MINUTES.toMillis(mMaintenanceWindowStart); + long windowEndMillis = TimeUnit.MINUTES.toMillis(mMaintenanceWindowEnd); + final long dayInMillis = TimeUnit.DAYS.toMillis(1); + + if ((windowStartMillis <= whenMillis && whenMillis <= windowEndMillis) + || ((windowStartMillis > windowEndMillis) + && (windowStartMillis <= whenMillis || whenMillis <= windowEndMillis))) { + return new InstallationOption(TYPE_INSTALL_AUTOMATIC, + (windowEndMillis - whenMillis + dayInMillis) % dayInMillis); + } else { + return new InstallationOption(TYPE_PAUSE, + (windowStartMillis - whenMillis + dayInMillis) % dayInMillis); + } + } else { + throw new RuntimeException("Unknown policy type"); + } + } + + private static LocalDate roundUpLeapDay(LocalDate date) { + if (date.isLeapYear() && date.getMonthValue() == 2 && date.getDayOfMonth() == 28) { + return date.plusDays(1); + } else { + return date; + } + } + + /** Convert a timestamp since epoch to a LocalDate using default timezone, truncating + * the hour/min/seconds part. + */ + private static LocalDate millisToDate(long when) { + return Instant.ofEpochMilli(when).atZone(ZoneId.systemDefault()).toLocalDate(); + } + + /** + * Returns the timestamp since epoch of a LocalDate, assuming the time is 00:00:00. + */ + private static long dateToMillis(LocalDate when) { + return LocalDateTime.of(when, LocalTime.MIN).atZone(ZoneId.systemDefault()).toInstant() + .toEpochMilli(); + } + @Override public String toString() { return String.format("SystemUpdatePolicy (type: %d, windowStart: %d, windowEnd: %d, " @@ -480,11 +652,13 @@ public class SystemUpdatePolicy implements Parcelable { mFreezePeriods.stream().map(n -> n.toString()).collect(Collectors.joining(","))); } + @SystemApi @Override public int describeContents() { return 0; } + @SystemApi @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mPolicyType); @@ -499,6 +673,7 @@ public class SystemUpdatePolicy implements Parcelable { } } + @SystemApi public static final Parcelable.Creator<SystemUpdatePolicy> CREATOR = new Parcelable.Creator<SystemUpdatePolicy>() { diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java index 8f49bc177dcb..1312a2e6b623 100644 --- a/core/java/android/app/assist/AssistStructure.java +++ b/core/java/android/app/assist/AssistStructure.java @@ -24,6 +24,7 @@ import android.text.TextUtils; import android.util.Log; import android.util.Pair; import android.view.View; +import android.view.View.AutofillImportance; import android.view.ViewRootImpl; import android.view.ViewStructure; import android.view.ViewStructure.HtmlInfo; @@ -632,6 +633,7 @@ public class AssistStructure implements Parcelable { int mMaxEms = -1; int mMaxLength = -1; @Nullable String mTextIdEntry; + @AutofillImportance int mImportantForAutofill; // POJO used to override some autofill-related values when the node is parcelized. // Not written to parcel. @@ -733,6 +735,7 @@ public class AssistStructure implements Parcelable { mMaxEms = in.readInt(); mMaxLength = in.readInt(); mTextIdEntry = preader.readString(); + mImportantForAutofill = in.readInt(); } if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) { mX = in.readInt(); @@ -900,6 +903,7 @@ public class AssistStructure implements Parcelable { out.writeInt(mMaxEms); out.writeInt(mMaxLength); pwriter.writeString(mTextIdEntry); + out.writeInt(mImportantForAutofill); } if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) { out.writeInt(mX); @@ -1299,6 +1303,17 @@ public class AssistStructure implements Parcelable { } /** + * @hide + */ + public void setWebDomain(@Nullable String domain) { + if (domain == null) return; + + final Uri uri = Uri.parse(domain); + mWebScheme = uri.getScheme(); + mWebDomain = uri.getHost(); + } + + /** * Returns the scheme of the HTML document represented by this view. * * <p>Typically used when the view associated with the view is a container for an HTML @@ -1512,6 +1527,16 @@ public class AssistStructure implements Parcelable { public int getMaxTextLength() { return mMaxLength; } + + /** + * Gets the {@link View#setImportantForAutofill(int) importantForAutofill mode} of + * the view associated with this node. + * + * <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes. + */ + public @AutofillImportance int getImportantForAutofill() { + return mImportantForAutofill; + } } /** @@ -1844,6 +1869,11 @@ public class AssistStructure implements Parcelable { } @Override + public void setImportantForAutofill(@AutofillImportance int mode) { + mNode.mImportantForAutofill = mode; + } + + @Override public void setInputType(int inputType) { mNode.mInputType = inputType; } @@ -1870,14 +1900,7 @@ public class AssistStructure implements Parcelable { @Override public void setWebDomain(@Nullable String domain) { - if (domain == null) { - mNode.mWebScheme = null; - mNode.mWebDomain = null; - return; - } - Uri uri = Uri.parse(domain); - mNode.mWebScheme = uri.getScheme(); - mNode.mWebDomain = uri.getHost(); + mNode.setWebDomain(domain); } @Override @@ -2144,7 +2167,8 @@ public class AssistStructure implements Parcelable { + ", options=" + Arrays.toString(node.getAutofillOptions()) + ", hints=" + Arrays.toString(node.getAutofillHints()) + ", value=" + node.getAutofillValue() - + ", sanitized=" + node.isSanitized()); + + ", sanitized=" + node.isSanitized() + + ", importantFor=" + node.getImportantForAutofill()); } final int NCHILDREN = node.getChildCount(); diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java index 72eb4948f0f9..d36a794ac046 100644 --- a/core/java/android/app/backup/BackupAgent.java +++ b/core/java/android/app/backup/BackupAgent.java @@ -163,6 +163,16 @@ public abstract class BackupAgent extends ContextWrapper { */ public static final int FLAG_DEVICE_TO_DEVICE_TRANSFER = 2; + /** + * Flag for {@link BackupDataOutput#getTransportFlags()} and + * {@link FullBackupDataOutput#getTransportFlags()} only. + * + * <p>Used for internal testing only. Do not check this flag in production code. + * + * @hide + */ + public static final int FLAG_FAKE_CLIENT_SIDE_ENCRYPTION_ENABLED = 1 << 31; + Handler mHandler = null; Handler getHandler() { diff --git a/core/java/android/app/backup/OWNERS b/core/java/android/app/backup/OWNERS new file mode 100644 index 000000000000..1c9a43acfa65 --- /dev/null +++ b/core/java/android/app/backup/OWNERS @@ -0,0 +1,7 @@ +artikz@google.com +brufino@google.com +bryanmawhinney@google.com +ctate@google.com +jorlow@google.com +mkarpinski@google.com + diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java index a1ad825c3a29..ee13880c9016 100644 --- a/core/java/android/app/job/JobInfo.java +++ b/core/java/android/app/job/JobInfo.java @@ -365,9 +365,7 @@ public class JobInfo implements Parcelable { /** @hide */ public boolean isExemptedFromAppStandby() { - return ((flags & FLAG_EXEMPT_FROM_APP_STANDBY) != 0) - && !hasEarlyConstraint() - && !hasLateConstraint(); + return ((flags & FLAG_EXEMPT_FROM_APP_STANDBY) != 0) && !isPeriodic(); } /** diff --git a/core/java/android/app/servertransaction/ActivityLifecycleItem.java b/core/java/android/app/servertransaction/ActivityLifecycleItem.java index 9a50a009ce34..7f8c50cd4ce5 100644 --- a/core/java/android/app/servertransaction/ActivityLifecycleItem.java +++ b/core/java/android/app/servertransaction/ActivityLifecycleItem.java @@ -91,4 +91,9 @@ public abstract class ActivityLifecycleItem extends ClientTransactionItem { pw.println(prefix + "target state:" + getTargetState()); pw.println(prefix + "description: " + mDescription); } + + @Override + public void recycle() { + setDescription(null); + } } diff --git a/core/java/android/app/servertransaction/ActivityRelaunchItem.java b/core/java/android/app/servertransaction/ActivityRelaunchItem.java new file mode 100644 index 000000000000..d8a7463cb086 --- /dev/null +++ b/core/java/android/app/servertransaction/ActivityRelaunchItem.java @@ -0,0 +1,176 @@ +/* + * Copyright 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.app.servertransaction; + +import static android.app.ActivityThread.DEBUG_ORDER; + +import android.app.ActivityThread; +import android.app.ClientTransactionHandler; +import android.app.ResultInfo; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Trace; +import android.util.MergedConfiguration; +import android.util.Slog; + +import com.android.internal.content.ReferrerIntent; + +import java.util.List; +import java.util.Objects; + +/** + * Activity relaunch callback. + * @hide + */ +public class ActivityRelaunchItem extends ClientTransactionItem { + + private static final String TAG = "ActivityRelaunchItem"; + + private List<ResultInfo> mPendingResults; + private List<ReferrerIntent> mPendingNewIntents; + private int mConfigChanges; + private MergedConfiguration mConfig; + private boolean mPreserveWindow; + + /** + * A record that was properly configured for relaunch. Execution will be cancelled if not + * initialized after {@link #preExecute(ClientTransactionHandler, IBinder)}. + */ + private ActivityThread.ActivityClientRecord mActivityClientRecord; + + @Override + public void preExecute(ClientTransactionHandler client, IBinder token) { + mActivityClientRecord = client.prepareRelaunchActivity(token, mPendingResults, + mPendingNewIntents, mConfigChanges, mConfig, mPreserveWindow); + } + + @Override + public void execute(ClientTransactionHandler client, IBinder token, + PendingTransactionActions pendingActions) { + if (mActivityClientRecord == null) { + if (DEBUG_ORDER) Slog.d(TAG, "Activity relaunch cancelled"); + return; + } + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart"); + client.handleRelaunchActivity(mActivityClientRecord, pendingActions); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + } + + @Override + public void postExecute(ClientTransactionHandler client, IBinder token, + PendingTransactionActions pendingActions) { + client.reportRelaunch(token, pendingActions); + } + + // ObjectPoolItem implementation + + private ActivityRelaunchItem() {} + + /** Obtain an instance initialized with provided params. */ + public static ActivityRelaunchItem obtain(List<ResultInfo> pendingResults, + List<ReferrerIntent> pendingNewIntents, int configChanges, MergedConfiguration config, + boolean preserveWindow) { + ActivityRelaunchItem instance = ObjectPool.obtain(ActivityRelaunchItem.class); + if (instance == null) { + instance = new ActivityRelaunchItem(); + } + instance.mPendingResults = pendingResults; + instance.mPendingNewIntents = pendingNewIntents; + instance.mConfigChanges = configChanges; + instance.mConfig = config; + instance.mPreserveWindow = preserveWindow; + + return instance; + } + + @Override + public void recycle() { + mPendingResults = null; + mPendingNewIntents = null; + mConfigChanges = 0; + mConfig = null; + mPreserveWindow = false; + mActivityClientRecord = null; + ObjectPool.recycle(this); + } + + + // Parcelable implementation + + /** Write to Parcel. */ + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeTypedList(mPendingResults, flags); + dest.writeTypedList(mPendingNewIntents, flags); + dest.writeInt(mConfigChanges); + dest.writeTypedObject(mConfig, flags); + dest.writeBoolean(mPreserveWindow); + } + + /** Read from Parcel. */ + private ActivityRelaunchItem(Parcel in) { + mPendingResults = in.createTypedArrayList(ResultInfo.CREATOR); + mPendingNewIntents = in.createTypedArrayList(ReferrerIntent.CREATOR); + mConfigChanges = in.readInt(); + mConfig = in.readTypedObject(MergedConfiguration.CREATOR); + mPreserveWindow = in.readBoolean(); + } + + public static final Creator<ActivityRelaunchItem> CREATOR = + new Creator<ActivityRelaunchItem>() { + public ActivityRelaunchItem createFromParcel(Parcel in) { + return new ActivityRelaunchItem(in); + } + + public ActivityRelaunchItem[] newArray(int size) { + return new ActivityRelaunchItem[size]; + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final ActivityRelaunchItem other = (ActivityRelaunchItem) o; + return Objects.equals(mPendingResults, other.mPendingResults) + && Objects.equals(mPendingNewIntents, other.mPendingNewIntents) + && mConfigChanges == other.mConfigChanges && Objects.equals(mConfig, other.mConfig) + && mPreserveWindow == other.mPreserveWindow; + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + Objects.hashCode(mPendingResults); + result = 31 * result + Objects.hashCode(mPendingNewIntents); + result = 31 * result + mConfigChanges; + result = 31 * result + Objects.hashCode(mConfig); + result = 31 * result + (mPreserveWindow ? 1 : 0); + return result; + } + + @Override + public String toString() { + return "ActivityRelaunchItem{pendingResults=" + mPendingResults + + ",pendingNewIntents=" + mPendingNewIntents + ",configChanges=" + mConfigChanges + + ",config=" + mConfig + ",preserveWindow" + mPreserveWindow + "}"; + } +} diff --git a/core/java/android/app/servertransaction/DestroyActivityItem.java b/core/java/android/app/servertransaction/DestroyActivityItem.java index cbcf6c750fed..0edcf1884f01 100644 --- a/core/java/android/app/servertransaction/DestroyActivityItem.java +++ b/core/java/android/app/servertransaction/DestroyActivityItem.java @@ -37,7 +37,7 @@ public class DestroyActivityItem extends ActivityLifecycleItem { PendingTransactionActions pendingActions) { Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityDestroy"); client.handleDestroyActivity(token, mFinished, mConfigChanges, - false /* getNonConfigInstance */); + false /* getNonConfigInstance */, getDescription()); Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); } @@ -65,6 +65,7 @@ public class DestroyActivityItem extends ActivityLifecycleItem { @Override public void recycle() { + super.recycle(); mFinished = false; mConfigChanges = 0; ObjectPool.recycle(this); diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java index 70a4755f99af..91e73cd5b1cd 100644 --- a/core/java/android/app/servertransaction/PauseActivityItem.java +++ b/core/java/android/app/servertransaction/PauseActivityItem.java @@ -102,6 +102,7 @@ public class PauseActivityItem extends ActivityLifecycleItem { @Override public void recycle() { + super.recycle(); mFinished = false; mUserLeaving = false; mConfigChanges = 0; diff --git a/core/java/android/app/servertransaction/PendingTransactionActions.java b/core/java/android/app/servertransaction/PendingTransactionActions.java index 073d28cfa27f..af7b7a21a582 100644 --- a/core/java/android/app/servertransaction/PendingTransactionActions.java +++ b/core/java/android/app/servertransaction/PendingTransactionActions.java @@ -44,6 +44,7 @@ public class PendingTransactionActions { private boolean mCallOnPostCreate; private Bundle mOldState; private StopInfo mStopInfo; + private boolean mReportRelaunchToWM; public PendingTransactionActions() { clear(); @@ -91,6 +92,24 @@ public class PendingTransactionActions { mStopInfo = stopInfo; } + /** + * Check if we should report an activity relaunch to WindowManager. We report back for every + * relaunch request to ActivityManager, but only for those that were actually finished to we + * report to WindowManager. + */ + public boolean shouldReportRelaunchToWindowManager() { + return mReportRelaunchToWM; + } + + /** + * Set if we should report an activity relaunch to WindowManager. We report back for every + * relaunch request to ActivityManager, but only for those that were actually finished we report + * to WindowManager. + */ + public void setReportRelaunchToWindowManager(boolean reportToWm) { + mReportRelaunchToWM = reportToWm; + } + /** Reports to server about activity stop. */ public static class StopInfo implements Runnable { private static final String TAG = "ActivityStopInfo"; diff --git a/core/java/android/app/servertransaction/ResumeActivityItem.java b/core/java/android/app/servertransaction/ResumeActivityItem.java index ed90f2cb1013..af2fb713e1bc 100644 --- a/core/java/android/app/servertransaction/ResumeActivityItem.java +++ b/core/java/android/app/servertransaction/ResumeActivityItem.java @@ -101,6 +101,7 @@ public class ResumeActivityItem extends ActivityLifecycleItem { @Override public void recycle() { + super.recycle(); mProcState = ActivityManager.PROCESS_STATE_UNKNOWN; mUpdateProcState = false; mIsForward = false; diff --git a/core/java/android/app/servertransaction/StopActivityItem.java b/core/java/android/app/servertransaction/StopActivityItem.java index b814d1ae1392..f955a903d649 100644 --- a/core/java/android/app/servertransaction/StopActivityItem.java +++ b/core/java/android/app/servertransaction/StopActivityItem.java @@ -72,6 +72,7 @@ public class StopActivityItem extends ActivityLifecycleItem { @Override public void recycle() { + super.recycle(); mShowWindow = false; mConfigChanges = 0; ObjectPool.recycle(this); diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java index 78b393a831f9..840fef80a5ff 100644 --- a/core/java/android/app/servertransaction/TransactionExecutor.java +++ b/core/java/android/app/servertransaction/TransactionExecutor.java @@ -194,7 +194,9 @@ public class TransactionExecutor { break; case ON_DESTROY: mTransactionHandler.handleDestroyActivity(r.token, false /* finishing */, - 0 /* configChanges */, false /* getNonConfigInstance */); + 0 /* configChanges */, false /* getNonConfigInstance */, + "performLifecycleSequence. cycling to:" + + mLifecycleSequence.get(size - 1)); break; case ON_RESTART: mTransactionHandler.performRestartActivity(r.token, false /* start */); diff --git a/core/java/android/app/timezone/RulesManager.java b/core/java/android/app/timezone/RulesManager.java index 0a38eb9ae777..dc7925694176 100644 --- a/core/java/android/app/timezone/RulesManager.java +++ b/core/java/android/app/timezone/RulesManager.java @@ -68,6 +68,23 @@ public final class RulesManager { private static final String TAG = "timezone.RulesManager"; private static final boolean DEBUG = false; + /** + * The action of the intent that the Android system will broadcast when a time zone rules update + * operation has been successfully staged (i.e. to be applied next reboot) or unstaged. + * + * <p>See {@link #EXTRA_OPERATION_STAGED} + * + * <p>This is a protected intent that can only be sent by the system. + */ + public static final String ACTION_RULES_UPDATE_OPERATION = + "com.android.intent.action.timezone.RULES_UPDATE_OPERATION"; + + /** + * The key for a boolean extra for the {@link #ACTION_RULES_UPDATE_OPERATION} intent used to + * indicate whether the operation was a "stage" or an "unstage". + */ + public static final String EXTRA_OPERATION_STAGED = "staged"; + @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = { "SUCCESS", "ERROR_" }, value = { SUCCESS, diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java index edb992bd265c..6b573e99f1fa 100644 --- a/core/java/android/app/usage/UsageEvents.java +++ b/core/java/android/app/usage/UsageEvents.java @@ -16,6 +16,7 @@ package android.app.usage; import android.annotation.IntDef; +import android.annotation.SystemApi; import android.content.res.Configuration; import android.os.Parcel; import android.os.Parcelable; @@ -104,12 +105,14 @@ public final class UsageEvents implements Parcelable { * An event type denoting that a notification was viewed by the user. * @hide */ + @SystemApi public static final int NOTIFICATION_SEEN = 10; /** * An event type denoting a change in App Standby Bucket. * @hide */ + @SystemApi public static final int STANDBY_BUCKET_CHANGED = 11; /** @hide */ @@ -257,6 +260,17 @@ public final class UsageEvents implements Parcelable { return mShortcutId; } + /** + * Returns the standby bucket of the app, if the event is of type + * {@link #STANDBY_BUCKET_CHANGED}, otherwise returns 0. + * @return the standby bucket associated with the event. + * @hide + */ + @SystemApi + public int getStandbyBucket() { + return mBucket; + } + /** @hide */ public Event getObfuscatedIfInstantApp() { if ((mFlags & FLAG_IS_PACKAGE_INSTANT_APP) == 0) { diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java index a2c75a6c014c..e736f34eea11 100644 --- a/core/java/android/appwidget/AppWidgetManager.java +++ b/core/java/android/appwidget/AppWidgetManager.java @@ -19,6 +19,7 @@ package android.appwidget; import android.annotation.BroadcastBehavior; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresFeature; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemService; @@ -29,6 +30,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentSender; import android.content.ServiceConnection; +import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.content.pm.ShortcutInfo; import android.os.Bundle; @@ -55,6 +57,7 @@ import java.util.List; * </div> */ @SystemService(Context.APPWIDGET_SERVICE) +@RequiresFeature(PackageManager.FEATURE_APP_WIDGETS) public class AppWidgetManager { /** diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index bc7823b00f49..1dc7549e763a 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -2307,6 +2307,9 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.HID_DEVICE) { BluetoothHidDevice hidDevice = new BluetoothHidDevice(context, listener); return true; + } else if (profile == BluetoothProfile.HEARING_AID) { + BluetoothHearingAid hearingAid = new BluetoothHearingAid(context, listener); + return true; } else { return false; } @@ -2389,6 +2392,9 @@ public final class BluetoothAdapter { BluetoothHidDevice hidDevice = (BluetoothHidDevice) proxy; hidDevice.close(); break; + case BluetoothProfile.HEARING_AID: + BluetoothHearingAid hearingAid = (BluetoothHearingAid) proxy; + hearingAid.close(); } } diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java new file mode 100644 index 000000000000..647e0d033fb7 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothHearingAid.java @@ -0,0 +1,693 @@ +/* + * 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.bluetooth; + +import android.Manifest; +import android.annotation.RequiresPermission; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Binder; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import com.android.internal.annotations.GuardedBy; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * This class provides the public APIs to control the Bluetooth Hearing Aid + * profile. + * + * <p>BluetoothHearingAid is a proxy object for controlling the Bluetooth Hearing Aid + * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get + * the BluetoothHearingAid proxy object. + * + * <p> Each method is protected with its appropriate permission. + * @hide + */ +public final class BluetoothHearingAid implements BluetoothProfile { + private static final String TAG = "BluetoothHearingAid"; + private static final boolean DBG = false; + private static final boolean VDBG = false; + + /** + * Intent used to broadcast the change in connection state of the Hearing Aid + * profile. + * + * <p>This intent will have 3 extras: + * <ul> + * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> + * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li> + * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> + * </ul> + * + * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CONNECTION_STATE_CHANGED = + "android.bluetooth.hearingaid.profile.action.CONNECTION_STATE_CHANGED"; + + /** + * Intent used to broadcast the change in the Playing state of the Hearing Aid + * profile. + * + * <p>This intent will have 3 extras: + * <ul> + * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> + * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li> + * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> + * </ul> + * + * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING}, + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_PLAYING_STATE_CHANGED = + "android.bluetooth.hearingaid.profile.action.PLAYING_STATE_CHANGED"; + + /** + * Intent used to broadcast the selection of a connected device as active. + * + * <p>This intent will have one extra: + * <ul> + * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can + * be null if no device is active. </li> + * </ul> + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_ACTIVE_DEVICE_CHANGED = + "android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED"; + + /** + * Hearing Aid device is streaming music. This state can be one of + * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of + * {@link #ACTION_PLAYING_STATE_CHANGED} intent. + */ + public static final int STATE_PLAYING = 10; + + /** + * Hearing Aid device is NOT streaming music. This state can be one of + * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of + * {@link #ACTION_PLAYING_STATE_CHANGED} intent. + */ + public static final int STATE_NOT_PLAYING = 11; + + /** This device represents Left Hearing Aid. */ + public static final int SIDE_LEFT = IBluetoothHearingAid.SIDE_LEFT; + + /** This device represents Right Hearing Aid. */ + public static final int SIDE_RIGHT = IBluetoothHearingAid.SIDE_RIGHT; + + /** This device is Monaural. */ + public static final int MODE_MONAURAL = IBluetoothHearingAid.MODE_MONAURAL; + + /** This device is Binaural (should receive only left or right audio). */ + public static final int MODE_BINAURAL = IBluetoothHearingAid.MODE_BINAURAL; + + /** Can't read ClientID for this device */ + public static final long HI_SYNC_ID_INVALID = IBluetoothHearingAid.HI_SYNC_ID_INVALID; + + private Context mContext; + private ServiceListener mServiceListener; + private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock(); + @GuardedBy("mServiceLock") + private IBluetoothHearingAid mService; + private BluetoothAdapter mAdapter; + + private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + new IBluetoothStateChangeCallback.Stub() { + public void onBluetoothStateChange(boolean up) { + if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); + if (!up) { + if (VDBG) Log.d(TAG, "Unbinding service..."); + try { + mServiceLock.writeLock().lock(); + mService = null; + mContext.unbindService(mConnection); + } catch (Exception re) { + Log.e(TAG, "", re); + } finally { + mServiceLock.writeLock().unlock(); + } + } else { + try { + mServiceLock.readLock().lock(); + if (mService == null) { + if (VDBG) Log.d(TAG, "Binding service..."); + doBind(); + } + } catch (Exception re) { + Log.e(TAG, "", re); + } finally { + mServiceLock.readLock().unlock(); + } + } + } + }; + + /** + * Create a BluetoothHearingAid proxy object for interacting with the local + * Bluetooth Hearing Aid service. + */ + /*package*/ BluetoothHearingAid(Context context, ServiceListener l) { + mContext = context; + mServiceListener = l; + mAdapter = BluetoothAdapter.getDefaultAdapter(); + IBluetoothManager mgr = mAdapter.getBluetoothManager(); + if (mgr != null) { + try { + mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } + + doBind(); + } + + void doBind() { + Intent intent = new Intent(IBluetoothHearingAid.class.getName()); + ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); + intent.setComponent(comp); + if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, + android.os.Process.myUserHandle())) { + Log.e(TAG, "Could not bind to Bluetooth Hearing Aid Service with " + intent); + return; + } + } + + /*package*/ void close() { + mServiceListener = null; + IBluetoothManager mgr = mAdapter.getBluetoothManager(); + if (mgr != null) { + try { + mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); + } catch (Exception e) { + Log.e(TAG, "", e); + } + } + + try { + mServiceLock.writeLock().lock(); + if (mService != null) { + mService = null; + mContext.unbindService(mConnection); + } + } catch (Exception re) { + Log.e(TAG, "", re); + } finally { + mServiceLock.writeLock().unlock(); + } + } + + @Override + public void finalize() { + // The empty finalize needs to be kept or the + // cts signature tests would fail. + } + + /** + * Initiate connection to a profile of the remote bluetooth device. + * + * <p> This API returns false in scenarios like the profile on the + * device is already connected or Bluetooth is not turned on. + * When this API returns true, it is guaranteed that + * connection state intent for the profile will be broadcasted with + * the state. Users can get the connection state of the profile + * from this intent. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, true otherwise + * @hide + */ + public boolean connect(BluetoothDevice device) { + if (DBG) log("connect(" + device + ")"); + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() && isValidDevice(device)) { + return mService.connect(device); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * Initiate disconnection from a profile + * + * <p> This API will return false in scenarios like the profile on the + * Bluetooth device is not in connected state etc. When this API returns, + * true, it is guaranteed that the connection state change + * intent will be broadcasted with the state. Users can get the + * disconnection state of the profile from this intent. + * + * <p> If the disconnection is initiated by a remote device, the state + * will transition from {@link #STATE_CONNECTED} to + * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the + * host (local) device the state will transition from + * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to + * state {@link #STATE_DISCONNECTED}. The transition to + * {@link #STATE_DISCONNECTING} can be used to distinguish between the + * two scenarios. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, true otherwise + * @hide + */ + public boolean disconnect(BluetoothDevice device) { + if (DBG) log("disconnect(" + device + ")"); + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() && isValidDevice(device)) { + return mService.disconnect(device); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public List<BluetoothDevice> getConnectedDevices() { + if (VDBG) log("getConnectedDevices()"); + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled()) { + return mService.getConnectedDevices(); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList<BluetoothDevice>(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList<BluetoothDevice>(); + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { + if (VDBG) log("getDevicesMatchingStates()"); + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled()) { + return mService.getDevicesMatchingConnectionStates(states); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList<BluetoothDevice>(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList<BluetoothDevice>(); + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public int getConnectionState(BluetoothDevice device) { + if (VDBG) log("getState(" + device + ")"); + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() + && isValidDevice(device)) { + return mService.getConnectionState(device); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.STATE_DISCONNECTED; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.STATE_DISCONNECTED; + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * Set priority of the profile + * + * <p> The device should already be paired. + * Priority can be one of {@link #PRIORITY_ON} orgetBluetoothManager + * {@link #PRIORITY_OFF}, + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Paired bluetooth device + * @param priority + * @return true if priority is set, false on error + * @hide + */ + public boolean setPriority(BluetoothDevice device, int priority) { + if (DBG) log("setPriority(" + device + ", " + priority + ")"); + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() + && isValidDevice(device)) { + if (priority != BluetoothProfile.PRIORITY_OFF + && priority != BluetoothProfile.PRIORITY_ON) { + return false; + } + return mService.setPriority(device, priority); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * Get the priority of the profile. + * + * <p> The priority can be any of: + * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF}, + * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} + * + * @param device Bluetooth device + * @return priority of the device + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public int getPriority(BluetoothDevice device) { + if (VDBG) log("getPriority(" + device + ")"); + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() + && isValidDevice(device)) { + return mService.getPriority(device); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.PRIORITY_OFF; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.PRIORITY_OFF; + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * Helper for converting a state to a string. + * + * For debug use only - strings are not internationalized. + * + * @hide + */ + public static String stateToString(int state) { + switch (state) { + case STATE_DISCONNECTED: + return "disconnected"; + case STATE_CONNECTING: + return "connecting"; + case STATE_CONNECTED: + return "connected"; + case STATE_DISCONNECTING: + return "disconnecting"; + case STATE_PLAYING: + return "playing"; + case STATE_NOT_PLAYING: + return "not playing"; + default: + return "<unknown state " + state + ">"; + } + } + + /** + * Get the volume of the device. + * + * <p> The volume is between -128 dB (mute) to 0 dB. + * + * @return volume of the hearing aid device. + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public int getVolume() { + if (VDBG) { + log("getVolume()"); + } + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled()) { + return mService.getVolume(); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return 0; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return 0; + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * Tells remote device to adjust volume. Uses the following values: + * <ul> + * <li>{@link AudioManager#ADJUST_LOWER}</li> + * <li>{@link AudioManager#ADJUST_RAISE}</li> + * <li>{@link AudioManager#ADJUST_MUTE}</li> + * <li>{@link AudioManager#ADJUST_UNMUTE}</li> + * </ul> + * + * @param direction One of the supported adjust values. + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public void adjustVolume(int direction) { + if (DBG) log("adjustVolume(" + direction + ")"); + + try { + mServiceLock.readLock().lock(); + + if (mService == null) { + Log.w(TAG, "Proxy not attached to service"); + return; + } + + if (!isEnabled()) return; + + mService.adjustVolume(direction); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * Tells remote device to set an absolute volume. + * + * @param volume Absolute volume to be set on remote + * @hide + */ + public void setVolume(int volume) { + if (DBG) Log.d(TAG, "setVolume(" + volume + ")"); + + try { + mServiceLock.readLock().lock(); + if (mService == null) { + Log.w(TAG, "Proxy not attached to service"); + return; + } + + if (!isEnabled()) return; + + mService.setVolume(volume); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * Get the CustomerId of the device. + * + * @param device Bluetooth device + * @return the CustomerId of the device + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public long getHiSyncId(BluetoothDevice device) { + if (VDBG) { + log("getCustomerId(" + device + ")"); + } + try { + mServiceLock.readLock().lock(); + if (mService == null) { + Log.w(TAG, "Proxy not attached to service"); + return HI_SYNC_ID_INVALID; + } + + if (!isEnabled() || !isValidDevice(device)) return HI_SYNC_ID_INVALID; + + return mService.getHiSyncId(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return HI_SYNC_ID_INVALID; + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * Get the side of the device. + * + * @param device Bluetooth device. + * @return SIDE_LEFT or SIDE_RIGHT + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public int getDeviceSide(BluetoothDevice device) { + if (VDBG) { + log("getDeviceSide(" + device + ")"); + } + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() + && isValidDevice(device)) { + return mService.getDeviceSide(device); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return SIDE_LEFT; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return SIDE_LEFT; + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * Get the mode of the device. + * + * @param device Bluetooth device + * @return MODE_MONAURAL or MODE_BINAURAL + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public int getDeviceMode(BluetoothDevice device) { + if (VDBG) { + log("getDeviceMode(" + device + ")"); + } + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() + && isValidDevice(device)) { + return mService.getDeviceMode(device); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return MODE_MONAURAL; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return MODE_MONAURAL; + } finally { + mServiceLock.readLock().unlock(); + } + } + + private final ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + if (DBG) Log.d(TAG, "Proxy object connected"); + try { + mServiceLock.writeLock().lock(); + mService = IBluetoothHearingAid.Stub.asInterface(Binder.allowBlocking(service)); + } finally { + mServiceLock.writeLock().unlock(); + } + + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.HEARING_AID, + BluetoothHearingAid.this); + } + } + + public void onServiceDisconnected(ComponentName className) { + if (DBG) Log.d(TAG, "Proxy object disconnected"); + try { + mServiceLock.writeLock().lock(); + mService = null; + } finally { + mServiceLock.writeLock().unlock(); + } + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.HEARING_AID); + } + } + }; + + private boolean isEnabled() { + if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; + return false; + } + + private boolean isValidDevice(BluetoothDevice device) { + if (device == null) return false; + + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; + return false; + } + + private static void log(String msg) { + Log.d(TAG, msg); + } +} diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java index 7e3bb05fe024..11f8ab7551c2 100644 --- a/core/java/android/bluetooth/BluetoothManager.java +++ b/core/java/android/bluetooth/BluetoothManager.java @@ -17,9 +17,11 @@ package android.bluetooth; import android.Manifest; +import android.annotation.RequiresFeature; import android.annotation.RequiresPermission; import android.annotation.SystemService; import android.content.Context; +import android.content.pm.PackageManager; import android.os.RemoteException; import android.util.Log; @@ -47,6 +49,7 @@ import java.util.List; * @see BluetoothAdapter#getDefaultAdapter() */ @SystemService(Context.BLUETOOTH_SERVICE) +@RequiresFeature(PackageManager.FEATURE_BLUETOOTH) public final class BluetoothManager { private static final String TAG = "BluetoothManager"; private static final boolean DBG = true; diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index 0e2263f773b8..656188fbdfb8 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -165,12 +165,19 @@ public interface BluetoothProfile { public static final int OPP = 20; /** + * Hearing Aid Device + * + * @hide + */ + int HEARING_AID = 21; + + /** * Max profile ID. This value should be updated whenever a new profile is added to match * the largest value assigned to a profile. * * @hide */ - public static final int MAX_PROFILE_ID = 20; + int MAX_PROFILE_ID = 21; /** * Default priority for devices that we try to auto-connect to and diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java index 76cb3f5b548e..0a0d21498032 100644 --- a/core/java/android/bluetooth/BluetoothUuid.java +++ b/core/java/android/bluetooth/BluetoothUuid.java @@ -79,6 +79,9 @@ public final class BluetoothUuid { ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid SAP = ParcelUuid.fromString("0000112D-0000-1000-8000-00805F9B34FB"); + /* TODO: b/69623109 update this value. It will change to 16bit UUID!! */ + public static final ParcelUuid HearingAid = + ParcelUuid.fromString("7312C48F-22CC-497F-85FD-A0616A3B9E05"); public static final ParcelUuid BASE_UUID = ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB"); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index b85a3199881c..f184380fd36c 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -560,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); } @@ -616,12 +617,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 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. */ - @Nullable + @NonNull public final Drawable getDrawable(@DrawableRes int id) { return getResources().getDrawable(id, getTheme()); } @@ -633,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()); } @@ -3539,6 +3538,7 @@ public abstract class Context { * @hide */ @SystemApi + @Deprecated public static final String WIFI_RTT_SERVICE = "rttmanager"; /** @@ -3671,10 +3671,8 @@ public abstract class Context { * * @see #getSystemService(String) * @see android.telephony.euicc.EuiccManager - * TODO(b/35851809): Unhide this API. - * @hide */ - public static final String EUICC_SERVICE = "euicc_service"; + public static final String EUICC_SERVICE = "euicc"; /** * Use with {@link #getSystemService(String)} to retrieve a @@ -3682,10 +3680,10 @@ public abstract class Context { * * @see #getSystemService(String) * @see android.telephony.euicc.EuiccCardManager - * TODO(b/35851809): Make this a SystemApi. * @hide */ - public static final String EUICC_CARD_SERVICE = "euicc_card_service"; + @SystemApi + public static final String EUICC_CARD_SERVICE = "euicc_card"; /** * Use with {@link #getSystemService(String)} to retrieve a @@ -4921,7 +4919,7 @@ public abstract class Context { /** * @hide */ - public void setAutofillClient(AutofillClient client) { + public void setAutofillClient(@SuppressWarnings("unused") AutofillClient client) { } /** @@ -4934,7 +4932,9 @@ public abstract class Context { /** * @hide */ - public void setAutofillCompatibilityEnabled(boolean autofillCompatEnabled) { + @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 a788989a7578..1867a6d879c7 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; @@ -1000,14 +1001,17 @@ public class ContextWrapper extends Context { */ @Override public boolean isAutofillCompatibilityEnabled() { - return mBase.isAutofillCompatibilityEnabled(); + return mBase != null && mBase.isAutofillCompatibilityEnabled(); } /** * @hide */ + @TestApi @Override public void setAutofillCompatibilityEnabled(boolean autofillCompatEnabled) { - mBase.setAutofillCompatibilityEnabled(autofillCompatEnabled); + if (mBase != null) { + mBase.setAutofillCompatibilityEnabled(autofillCompatEnabled); + } } } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index fa73e3cbab99..e7aead17d6ed 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2399,6 +2399,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. * @@ -2478,6 +2498,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. 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 8464e26ec6cd..6e63342698b3 100644 --- a/core/java/android/content/om/OverlayInfo.java +++ b/core/java/android/content/om/OverlayInfo.java @@ -18,6 +18,7 @@ package android.content.om; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -66,14 +67,14 @@ 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; @@ -90,6 +91,11 @@ public final class OverlayInfo implements Parcelable { public static final int STATE_OVERLAY_UPGRADING = 5; /** + * Category for theme overlays. + */ + public static final String CATEGORY_THEME = "android.theme"; + + /** * Package name of the overlay package */ public final String packageName; @@ -100,6 +106,11 @@ 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; @@ -121,14 +132,15 @@ public final class OverlayInfo implements Parcelable { * @param state the new state for the source OverlayInfo */ public OverlayInfo(@NonNull OverlayInfo source, @State int state) { - this(source.packageName, source.targetPackageName, source.baseCodePath, state, - source.userId); + this(source.packageName, source.targetPackageName, source.category, source.baseCodePath, + state, source.userId); } public OverlayInfo(@NonNull String packageName, @NonNull String targetPackageName, - @NonNull String baseCodePath, @State int state, int userId) { + @Nullable String category, @NonNull String baseCodePath, int state, int userId) { this.packageName = packageName; this.targetPackageName = targetPackageName; + this.category = category; this.baseCodePath = baseCodePath; this.state = state; this.userId = userId; @@ -138,6 +150,7 @@ public final class OverlayInfo implements Parcelable { public OverlayInfo(Parcel source) { packageName = source.readString(); targetPackageName = source.readString(); + category = source.readString(); baseCodePath = source.readString(); state = source.readInt(); userId = source.readInt(); @@ -177,6 +190,7 @@ 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); @@ -275,6 +289,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/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index e115f896137c..0e50002288cd 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -1631,7 +1631,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * @hide */ public boolean isAllowedToUseHiddenApi() { - return isSystemApp(); + return isSystemApp() || isUpdatedSystemApp(); } /** 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 112c5dae6731..3a95a5f87d97 100644 --- a/core/java/android/content/pm/InstantAppResolveInfo.java +++ b/core/java/android/content/pm/InstantAppResolveInfo.java @@ -26,10 +26,13 @@ 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; /** * Describes an externally resolvable instant application. There are three states that this class @@ -227,14 +230,25 @@ public final class InstantAppResolveInfo implements Parcelable { */ @SystemApi public static final class InstantAppDigest implements Parcelable { - private static final int DIGEST_MASK = 0xfffff000; - + static final int DIGEST_MASK = 0xfffff000; 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*/); @@ -306,6 +320,7 @@ public final class InstantAppResolveInfo implements Parcelable { } } mDigestPrefix = in.createIntArray(); + mDigestPrefixSecure = in.createIntArray(); } public byte[][] getDigestBytes() { @@ -316,6 +331,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; @@ -323,6 +358,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 { @@ -332,6 +372,7 @@ public final class InstantAppResolveInfo implements Parcelable { } } out.writeIntArray(mDigestPrefix); + out.writeIntArray(mDigestPrefixSecure); } @SuppressWarnings("hiding") @@ -339,6 +380,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/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java index 09a46b8acf4b..627ceb7871f2 100644 --- a/core/java/android/content/pm/PackageInfo.java +++ b/core/java/android/content/pm/PackageInfo.java @@ -362,6 +362,13 @@ public class PackageInfo implements Parcelable { */ public String overlayTarget; + /** + * The overlay category, if any, of this package + * + * @hide + */ + public String overlayCategory; + /** @hide */ public int overlayPriority; @@ -464,10 +471,23 @@ public class PackageInfo implements Parcelable { dest.writeString(restrictedAccountType); dest.writeString(requiredAccountType); dest.writeString(overlayTarget); + dest.writeString(overlayCategory); dest.writeInt(overlayPriority); dest.writeBoolean(mOverlayIsStatic); dest.writeInt(compileSdkVersion); dest.writeString(compileSdkVersionCodename); + writeSigningCertificateHistoryToParcel(dest, parcelableFlags); + } + + private void writeSigningCertificateHistoryToParcel(Parcel dest, int parcelableFlags) { + if (signingCertificateHistory != null) { + dest.writeInt(signingCertificateHistory.length); + for (int i = 0; i < signingCertificateHistory.length; i++) { + dest.writeTypedArray(signingCertificateHistory[i], parcelableFlags); + } + } else { + dest.writeInt(-1); + } } public static final Parcelable.Creator<PackageInfo> CREATOR @@ -519,10 +539,12 @@ public class PackageInfo implements Parcelable { restrictedAccountType = source.readString(); requiredAccountType = source.readString(); overlayTarget = source.readString(); + overlayCategory = source.readString(); overlayPriority = source.readInt(); mOverlayIsStatic = source.readBoolean(); compileSdkVersion = source.readInt(); compileSdkVersionCodename = source.readString(); + readSigningCertificateHistoryFromParcel(source); // The component lists were flattened with the redundant ApplicationInfo // instances omitted. Distribute the canonical one here as appropriate. @@ -534,6 +556,16 @@ public class PackageInfo implements Parcelable { } } + private void readSigningCertificateHistoryFromParcel(Parcel source) { + int len = source.readInt(); + if (len != -1) { + signingCertificateHistory = new Signature[len][]; + for (int i = 0; i < len; i++) { + signingCertificateHistory[i] = source.createTypedArray(Signature.CREATOR); + } + } + } + private void propagateApplicationInfo(ApplicationInfo appInfo, ComponentInfo[] components) { if (components != null) { for (ComponentInfo ci : components) { diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 486c86cedba3..07a991188a49 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -2070,6 +2070,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. @@ -2108,8 +2117,6 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device * supports embedded subscriptions on eUICCs. - * TODO(b/35851809): Make this public. - * @hide */ @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_TELEPHONY_EUICC = "android.hardware.telephony.euicc"; @@ -5067,6 +5074,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); diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 3afc346af36e..9e4166eaec1b 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -196,10 +196,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"; /** @@ -680,6 +676,7 @@ public class PackageParser { pi.restrictedAccountType = p.mRestrictedAccountType; pi.requiredAccountType = p.mRequiredAccountType; pi.overlayTarget = p.mOverlayTarget; + pi.overlayCategory = p.mOverlayCategory; pi.overlayPriority = p.mOverlayPriority; pi.mOverlayIsStatic = p.mOverlayIsStatic; pi.compileSdkVersion = p.mCompileSdkVersion; @@ -2077,6 +2074,8 @@ 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); @@ -4430,24 +4429,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 { @@ -4942,7 +4923,7 @@ public class PackageParser { p.info.authority = cpname.intern(); if (!parseProviderTags( - res, parser, visibleToEphemeral, owner, p, outError)) { + res, parser, visibleToEphemeral, p, outError)) { return null; } @@ -4950,7 +4931,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; @@ -4978,17 +4959,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, @@ -5277,17 +5247,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 && 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>: " @@ -6368,6 +6327,7 @@ public class PackageParser { public String mRequiredAccountType; public String mOverlayTarget; + public String mOverlayCategory; public int mOverlayPriority; public boolean mOverlayIsStatic; @@ -6878,6 +6838,7 @@ public class PackageParser { mRestrictedAccountType = dest.readString(); mRequiredAccountType = dest.readString(); mOverlayTarget = dest.readString(); + mOverlayCategory = dest.readString(); mOverlayPriority = dest.readInt(); mOverlayIsStatic = (dest.readInt() == 1); mCompileSdkVersion = dest.readInt(); @@ -7001,6 +6962,7 @@ public class PackageParser { dest.writeString(mRestrictedAccountType); dest.writeString(mRequiredAccountType); dest.writeString(mOverlayTarget); + dest.writeString(mOverlayCategory); dest.writeInt(mOverlayPriority); dest.writeInt(mOverlayIsStatic ? 1 : 0); dest.writeInt(mCompileSdkVersion); 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/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/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 78665609bdd4..5f8a34d46ecd 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -28,6 +28,8 @@ import android.util.Log; import android.util.SparseArray; import android.util.TypedValue; +import com.android.internal.annotations.GuardedBy; + import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.IOException; @@ -915,6 +917,7 @@ public final class AssetManager implements AutoCloseable { private native final void init(boolean isSystem); private native final void destroy(); + @GuardedBy("this") private final void incRefsLocked(long id) { if (DEBUG_REFS) { if (mRefStacks == null) { @@ -926,7 +929,8 @@ public final class AssetManager implements AutoCloseable { } mNumRefs++; } - + + @GuardedBy("this") private final void decRefsLocked(long id) { if (DEBUG_REFS && mRefStacks != null) { mRefStacks.remove(id); diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index e173653cd961..ad85e71b86f9 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -867,8 +867,9 @@ public class Resources { * @param theme The theme used to style the drawable attributes, may be {@code null}. * @return Drawable An object that can be used to draw this resource. * @throws NotFoundException Throws NotFoundException if the given ID does - * not exist. + * not exist, or cannot be decoded. */ + @NonNull public Drawable getDrawableForDensity(@DrawableRes int id, int density, @Nullable Theme theme) { final TypedValue value = obtainTempTypedValue(); try { @@ -980,7 +981,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 +1012,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 +1025,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 +1034,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 +1140,7 @@ public class Resources { * * @see #getXml */ + @NonNull public XmlResourceParser getLayout(@LayoutRes int id) throws NotFoundException { return loadXmlResourceParser(id, "layout"); } @@ -1163,6 +1165,7 @@ public class Resources { * * @see #getXml */ + @NonNull public XmlResourceParser getAnimation(@AnimatorRes @AnimRes int id) throws NotFoundException { return loadXmlResourceParser(id, "anim"); } @@ -1188,6 +1191,7 @@ public class Resources { * * @see android.util.AttributeSet */ + @NonNull public XmlResourceParser getXml(@XmlRes int id) throws NotFoundException { return loadXmlResourceParser(id, "xml"); } @@ -1203,8 +1207,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 +1265,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); diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index 97cb78bc4243..424fa833cd48 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; @@ -543,7 +545,7 @@ public class ResourcesImpl { } } - @Nullable + @NonNull Drawable loadDrawable(@NonNull Resources wrapper, @NonNull TypedValue value, int id, int density, @Nullable Resources.Theme theme) throws NotFoundException { @@ -627,7 +629,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 @@ -752,10 +754,31 @@ public class ResourcesImpl { } /** + * Loads a Drawable from an encoded image stream, or null. + * + * This call will handle closing ais. + */ + 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. */ + @NonNull 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); @@ -774,22 +797,23 @@ 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); @@ -804,13 +828,13 @@ public class ResourcesImpl { if (file.endsWith(".xml")) { final XmlResourceParser rp = loadXmlResourceParser( file, id, value.assetCookie, "drawable"); - dr = Drawable.createFromXmlForDensity(wrapper, rp, density, theme); + dr = Drawable.createFromXmlForDensity(wrapper, rp, density, null); rp.close(); } else { final InputStream is = mAssets.openNonAsset( value.assetCookie, file, AssetManager.ACCESS_STREAMING); - dr = Drawable.createFromResourceStream(wrapper, value, is, file, null); - is.close(); + AssetInputStream ais = (AssetInputStream) is; + dr = decodeImageDrawable(ais, wrapper, value); } } finally { stack.pop(); @@ -834,7 +858,7 @@ public class ResourcesImpl { final long loadedBitmapSize = Bitmap.sPreloadTracingTotalBitmapsSize - startBitmapSize; final int loadedDrawables = - sPreloadTracingNumLoadedDrawables - startDrwableCount; + sPreloadTracingNumLoadedDrawables - startDrawableCount; sPreloadTracingNumLoadedDrawables++; @@ -910,6 +934,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; @@ -985,7 +1010,7 @@ public class ResourcesImpl { return complexColor; } - @Nullable + @NonNull ColorStateList loadColorStateList(Resources wrapper, TypedValue value, int id, Resources.Theme theme) throws NotFoundException { @@ -1043,9 +1068,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) { 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. */ diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java index b211700328b9..dc60612451d4 100644 --- a/core/java/android/database/sqlite/SQLiteConnectionPool.java +++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java @@ -422,6 +422,7 @@ public final class SQLiteConnectionPool implements Closeable { } // Can't throw. + @GuardedBy("mLock") private boolean recycleConnectionLocked(SQLiteConnection connection, AcquiredConnectionStatus status) { if (status == AcquiredConnectionStatus.RECONFIGURE) { @@ -531,6 +532,7 @@ public final class SQLiteConnectionPool implements Closeable { } // Can't throw. + @GuardedBy("mLock") private void closeAvailableConnectionsAndLogExceptionsLocked() { closeAvailableNonPrimaryConnectionsAndLogExceptionsLocked(); @@ -541,6 +543,7 @@ public final class SQLiteConnectionPool implements Closeable { } // Can't throw. + @GuardedBy("mLock") private boolean closeAvailableConnectionLocked(int connectionId) { final int count = mAvailableNonPrimaryConnections.size(); for (int i = count - 1; i >= 0; i--) { @@ -562,6 +565,7 @@ public final class SQLiteConnectionPool implements Closeable { } // Can't throw. + @GuardedBy("mLock") private void closeAvailableNonPrimaryConnectionsAndLogExceptionsLocked() { final int count = mAvailableNonPrimaryConnections.size(); for (int i = 0; i < count; i++) { @@ -581,6 +585,7 @@ public final class SQLiteConnectionPool implements Closeable { } // Can't throw. + @GuardedBy("mLock") private void closeExcessConnectionsAndLogExceptionsLocked() { int availableCount = mAvailableNonPrimaryConnections.size(); while (availableCount-- > mMaxConnectionPoolSize - 1) { @@ -591,6 +596,7 @@ public final class SQLiteConnectionPool implements Closeable { } // Can't throw. + @GuardedBy("mLock") private void closeConnectionAndLogExceptionsLocked(SQLiteConnection connection) { try { connection.close(); // might throw @@ -609,6 +615,7 @@ public final class SQLiteConnectionPool implements Closeable { } // Can't throw. + @GuardedBy("mLock") private void reconfigureAllConnectionsLocked() { if (mAvailablePrimaryConnection != null) { try { @@ -776,6 +783,7 @@ public final class SQLiteConnectionPool implements Closeable { } // Can't throw. + @GuardedBy("mLock") private void cancelConnectionWaiterLocked(ConnectionWaiter waiter) { if (waiter.mAssignedConnection != null || waiter.mException != null) { // Waiter is done waiting but has not woken up yet. @@ -848,6 +856,7 @@ public final class SQLiteConnectionPool implements Closeable { } // Can't throw. + @GuardedBy("mLock") private void wakeConnectionWaitersLocked() { // Unpark all waiters that have requests that we can fulfill. // This method is designed to not throw runtime exceptions, although we might send @@ -910,6 +919,7 @@ public final class SQLiteConnectionPool implements Closeable { } // Might throw. + @GuardedBy("mLock") private SQLiteConnection tryAcquirePrimaryConnectionLocked(int connectionFlags) { // If the primary connection is available, acquire it now. SQLiteConnection connection = mAvailablePrimaryConnection; @@ -935,6 +945,7 @@ public final class SQLiteConnectionPool implements Closeable { } // Might throw. + @GuardedBy("mLock") private SQLiteConnection tryAcquireNonPrimaryConnectionLocked( String sql, int connectionFlags) { // Try to acquire the next connection in the queue. @@ -974,6 +985,7 @@ public final class SQLiteConnectionPool implements Closeable { } // Might throw. + @GuardedBy("mLock") private void finishAcquireConnectionLocked(SQLiteConnection connection, int connectionFlags) { try { final boolean readOnly = (connectionFlags & CONNECTION_FLAG_READ_ONLY) != 0; diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java index c1c0812e129e..ae1f57d62228 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -2006,7 +2006,6 @@ public final class SQLiteDatabase extends SQLiteClosable { * SQLiteDatabase db = SQLiteDatabase.openDatabase("db_filename", cursorFactory, * SQLiteDatabase.CREATE_IF_NECESSARY | SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING, * myDatabaseErrorHandler); - * db.enableWriteAheadLogging(); * </pre></code> * </p><p> * Another way to enable write-ahead logging is to call {@link #enableWriteAheadLogging} diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java index a2991e6e9cb6..64e9e5db7c4b 100644 --- a/core/java/android/database/sqlite/SQLiteOpenHelper.java +++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java @@ -58,7 +58,7 @@ public abstract class SQLiteOpenHelper { private SQLiteDatabase mDatabase; private boolean mIsInitializing; - private final SQLiteDatabase.OpenParams.Builder mOpenParamsBuilder; + private SQLiteDatabase.OpenParams.Builder mOpenParamsBuilder; /** * Create a helper object to create, open, and/or manage a database. @@ -163,8 +163,7 @@ public abstract class SQLiteOpenHelper { mName = name; mNewVersion = version; mMinimumSupportedVersion = Math.max(0, minimumSupportedVersion); - mOpenParamsBuilder = openParamsBuilder; - mOpenParamsBuilder.addOpenFlags(SQLiteDatabase.CREATE_IF_NECESSARY); + setOpenParamsBuilder(openParamsBuilder); } /** @@ -230,6 +229,30 @@ public abstract class SQLiteOpenHelper { } /** + * Sets configuration parameters that are used for opening {@link SQLiteDatabase}. + * <p>Please note that {@link SQLiteDatabase#CREATE_IF_NECESSARY} flag will always be set when + * opening the database + * + * @param openParams configuration parameters that are used for opening {@link SQLiteDatabase}. + * @throws IllegalStateException if the database is already open + */ + public void setOpenParams(@NonNull SQLiteDatabase.OpenParams openParams) { + Preconditions.checkNotNull(openParams); + synchronized (this) { + if (mDatabase != null && mDatabase.isOpen()) { + throw new IllegalStateException( + "OpenParams cannot be set after opening the database"); + } + setOpenParamsBuilder(new SQLiteDatabase.OpenParams.Builder(openParams)); + } + } + + private void setOpenParamsBuilder(SQLiteDatabase.OpenParams.Builder openParamsBuilder) { + mOpenParamsBuilder = openParamsBuilder; + mOpenParamsBuilder.addOpenFlags(SQLiteDatabase.CREATE_IF_NECESSARY); + } + + /** * Sets the maximum number of milliseconds that SQLite connection is allowed to be idle * before it is closed and removed from the pool. * diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 931b5c913851..f08e1cc24b26 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -242,6 +242,9 @@ public class Camera { /** * Returns the number of physical cameras available on this device. + * The return value of this method might change dynamically if the device + * supports external cameras and an external camera is connected or + * disconnected. * * @return total number of accessible camera devices, or 0 if there are no * cameras or an error was encountered enumerating them. @@ -3542,8 +3545,8 @@ public class Camera { /** * Gets the horizontal angle of view in degrees. * - * @return horizontal angle of view. This method will always return a - * valid value. + * @return horizontal angle of view. Returns -1.0 when the device + * doesn't report view angle information. */ public float getHorizontalViewAngle() { return Float.parseFloat(get(KEY_HORIZONTAL_VIEW_ANGLE)); @@ -3552,8 +3555,8 @@ public class Camera { /** * Gets the vertical angle of view in degrees. * - * @return vertical angle of view. This method will always return a - * valid value. + * @return vertical angle of view. Returns -1.0 when the device + * doesn't report view angle information. */ public float getVerticalViewAngle() { return Float.parseFloat(get(KEY_VERTICAL_VIEW_ANGLE)); diff --git a/core/java/android/hardware/ConsumerIrManager.java b/core/java/android/hardware/ConsumerIrManager.java index c7a33ffa1b0f..6f589cd9190b 100644 --- a/core/java/android/hardware/ConsumerIrManager.java +++ b/core/java/android/hardware/ConsumerIrManager.java @@ -16,8 +16,10 @@ package android.hardware; +import android.annotation.RequiresFeature; import android.annotation.SystemService; import android.content.Context; +import android.content.pm.PackageManager; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; @@ -27,6 +29,7 @@ import android.util.Log; * Class that operates consumer infrared on the device. */ @SystemService(Context.CONSUMER_IR_SERVICE) +@RequiresFeature(PackageManager.FEATURE_CONSUMER_IR) public final class ConsumerIrManager { private static final String TAG = "ConsumerIr"; diff --git a/core/java/android/hardware/OWNERS b/core/java/android/hardware/OWNERS new file mode 100644 index 000000000000..b8fea556e4c5 --- /dev/null +++ b/core/java/android/hardware/OWNERS @@ -0,0 +1,7 @@ +# Camera +per-file *Camera* = cychen@google.com +per-file *Camera* = epeev@google.com +per-file *Camera* = etalvala@google.com +per-file *Camera* = shuzhenwang@google.com +per-file *Camera* = yinchiayeh@google.com +per-file *Camera* = zhijunhe@google.com diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 96d043c2a894..8502fc413c05 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -341,7 +341,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri */ @SuppressWarnings({"unchecked"}) public List<CaptureRequest.Key<?>> getAvailablePhysicalCameraRequestKeys() { - if (mAvailableSessionKeys == null) { + if (mAvailablePhysicalRequestKeys == null) { Object crKey = CaptureRequest.Key.class; Class<CaptureRequest.Key<?>> crKeyTyped = (Class<CaptureRequest.Key<?>>)crKey; @@ -1372,9 +1372,6 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <p>The origin for {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}.</p> * <p>Different calibration methods and use cases can produce better or worse results * depending on the selected coordinate origin.</p> - * <p>For devices designed to support the MOTION_TRACKING capability, the GYROSCOPE origin - * makes device calibration and later usage by applications combining camera and gyroscope - * information together simpler.</p> * <p><b>Possible values:</b> * <ul> * <li>{@link #LENS_POSE_REFERENCE_PRIMARY_CAMERA PRIMARY_CAMERA}</li> @@ -1793,11 +1790,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * The respective value of such request key can be obtained by calling * {@link CaptureRequest.Builder#getPhysicalCameraKey }. Capture requests that contain * individual physical device requests must be built via - * {@link android.hardware.camera2.CameraDevice#createCaptureRequest(int, Set)}. - * Such extended capture requests can be passed only to - * {@link CameraCaptureSession#capture } or {@link CameraCaptureSession#captureBurst } and - * not to {@link CameraCaptureSession#setRepeatingRequest } or - * {@link CameraCaptureSession#setRepeatingBurst }.</p> + * {@link android.hardware.camera2.CameraDevice#createCaptureRequest(int, Set)}.</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> * <p><b>Limited capability</b> - * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index df644012ffb7..72db33f90c20 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -145,37 +145,6 @@ public abstract class CameraDevice implements AutoCloseable { */ public static final int TEMPLATE_MANUAL = 6; - /** - * A template for selecting camera parameters that match TEMPLATE_PREVIEW as closely as - * possible while improving the camera output for motion tracking use cases. - * - * <p>This template is best used by applications that are frequently switching between motion - * tracking use cases and regular still capture use cases, to minimize the IQ changes - * when swapping use cases.</p> - * - * <p>This template is guaranteed to be supported on camera devices that support the - * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING MOTION_TRACKING} - * capability.</p> - * - * @see #createCaptureRequest - */ - public static final int TEMPLATE_MOTION_TRACKING_PREVIEW = 7; - - /** - * A template for selecting camera parameters that maximize the quality of camera output for - * motion tracking use cases. - * - * <p>This template is best used by applications dedicated to motion tracking applications, - * which aren't concerned about fast switches between motion tracking and other use cases.</p> - * - * <p>This template is guaranteed to be supported on camera devices that support the - * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING MOTION_TRACKING} - * capability.</p> - * - * @see #createCaptureRequest - */ - public static final int TEMPLATE_MOTION_TRACKING_BEST = 8; - /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"TEMPLATE_"}, value = @@ -184,9 +153,7 @@ public abstract class CameraDevice implements AutoCloseable { TEMPLATE_RECORD, TEMPLATE_VIDEO_SNAPSHOT, TEMPLATE_ZERO_SHUTTER_LAG, - TEMPLATE_MANUAL, - TEMPLATE_MOTION_TRACKING_PREVIEW, - TEMPLATE_MOTION_TRACKING_BEST}) + TEMPLATE_MANUAL}) public @interface RequestTemplate {}; /** @@ -420,27 +387,6 @@ public abstract class CameraDevice implements AutoCloseable { * </table><br> * </p> * - * <p>MOTION_TRACKING-capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES} - * includes - * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING MOTION_TRACKING}) - * devices support at least the below stream combinations in addition to those for - * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED} devices. The - * {@code FULL FOV 640} entry means that the device will support a resolution that's 640 pixels - * wide, with the height set so that the resolution aspect ratio matches the MAXIMUM output - * aspect ratio, rounded down. So for a device with a 4:3 image sensor, this will be 640x480, - * and for a device with a 16:9 sensor, this will be 640x360, and so on. And the - * {@code MAX 30FPS} entry means the largest JPEG resolution on the device for which - * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration} - * returns a value less than or equal to 1/30s. - * - * <table> - * <tr><th colspan="7">MOTION_TRACKING-capability additional guaranteed configurations</th></tr> - * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th><th rowspan="2">Sample use case(s)</th> </tr> - * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr> - * <tr> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code FULL FOV 640}</td> <td>{@code JPEG}</td><td id="rb">{@code MAX 30FPS}</td> <td>Preview with a tracking YUV output and a as-large-as-possible JPEG for still captures.</td> </tr> - * </table><br> - * </p> - * * <p>BURST-capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES} includes * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE BURST_CAPTURE}) devices * support at least the below stream combinations in addition to those for @@ -917,11 +863,8 @@ public abstract class CameraDevice implements AutoCloseable { * request for a specific physical camera. The settings are chosen * to be the best options for the specific logical camera device. If * additional physical camera ids are passed, then they will also use the - * same settings template. Requests containing individual physical camera - * settings can be passed only to {@link CameraCaptureSession#capture} or - * {@link CameraCaptureSession#captureBurst} and not to - * {@link CameraCaptureSession#setRepeatingRequest} or - * {@link CameraCaptureSession#setRepeatingBurst}</p> + * same settings template. Clients can further modify individual camera + * settings by calling {@link CaptureRequest.Builder#setPhysicalCameraKey}.</p> * * <p>Individual physical camera settings will only be honored for camera session * that was initialiazed with corresponding physical camera id output configuration @@ -950,8 +893,8 @@ public abstract class CameraDevice implements AutoCloseable { * @see #TEMPLATE_STILL_CAPTURE * @see #TEMPLATE_VIDEO_SNAPSHOT * @see #TEMPLATE_MANUAL - * @see CaptureRequest.Builder#setKey - * @see CaptureRequest.Builder#getKey + * @see CaptureRequest.Builder#setPhysicalCameraKey + * @see CaptureRequest.Builder#getPhysicalCameraKey */ @NonNull public CaptureRequest.Builder createCaptureRequest(@RequestTemplate int templateType, diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index e7c8961186ec..e558b7e29a20 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -342,7 +342,7 @@ public abstract class CameraMetadata<TKey> { /** * <p>The value of {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation} is relative to the optical center of * the largest camera device facing the same direction as this camera.</p> - * <p>This default value for API levels before Android P.</p> + * <p>This is the default value for API levels before Android P.</p> * * @see CameraCharacteristics#LENS_POSE_TRANSLATION * @see CameraCharacteristics#LENS_POSE_REFERENCE @@ -352,7 +352,6 @@ public abstract class CameraMetadata<TKey> { /** * <p>The value of {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation} is relative to the position of the * primary gyroscope of this Android device.</p> - * <p>This is the value reported by all devices that support the MOTION_TRACKING capability.</p> * * @see CameraCharacteristics#LENS_POSE_TRANSLATION * @see CameraCharacteristics#LENS_POSE_REFERENCE @@ -801,46 +800,12 @@ public abstract class CameraMetadata<TKey> { public static final int REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO = 9; /** - * <p>The device supports controls and metadata required for accurate motion tracking for - * use cases such as augmented reality, electronic image stabilization, and so on.</p> - * <p>This means this camera device has accurate optical calibration and timestamps relative - * to the inertial sensors.</p> - * <p>This capability requires the camera device to support the following:</p> - * <ul> - * <li>Capture request templates {@link android.hardware.camera2.CameraDevice#TEMPLATE_MOTION_TRACKING_PREVIEW } and {@link android.hardware.camera2.CameraDevice#TEMPLATE_MOTION_TRACKING_BEST } are defined.</li> - * <li>The stream configurations listed in {@link android.hardware.camera2.CameraDevice#createCaptureSession } for MOTION_TRACKING are - * supported, either at 30 or 60fps maximum frame rate.</li> - * <li>The following camera characteristics and capture result metadata are provided:<ul> - * <li>{@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}</li> - * <li>{@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}</li> - * <li>{@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}</li> - * <li>{@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}</li> - * <li>{@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference} with value GYROSCOPE</li> - * </ul> - * </li> - * <li>The {@link CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE android.sensor.info.timestampSource} field has value <code>REALTIME</code>. When compared to - * timestamps from the device's gyroscopes, the clock difference for events occuring at - * the same actual time instant will be less than 1 ms.</li> - * <li>The value of the {@link CaptureResult#SENSOR_ROLLING_SHUTTER_SKEW android.sensor.rollingShutterSkew} field is accurate to within 1 ms.</li> - * <li>The value of {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime} is guaranteed to be available in the - * capture result.</li> - * <li>The {@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent} control supports MOTION_TRACKING to limit maximum - * exposure to 20 milliseconds.</li> - * <li>The stream configurations required for MOTION_TRACKING (listed at {@link android.hardware.camera2.CameraDevice#createCaptureSession }) can operate at least at - * 30fps; optionally, they can operate at 60fps, and '[60, 60]' is listed in - * {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES android.control.aeAvailableTargetFpsRanges}.</li> - * </ul> + * <p>The camera device supports the MOTION_TRACKING value for + * {@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent}, which limits maximum exposure time to 20 ms.</p> + * <p>This limits the motion blur of capture images, resulting in better image tracking + * results for use cases such as image stabilization or augmented reality.</p> * - * @see CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES * @see CaptureRequest#CONTROL_CAPTURE_INTENT - * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION - * @see CameraCharacteristics#LENS_POSE_REFERENCE - * @see CameraCharacteristics#LENS_POSE_ROTATION - * @see CameraCharacteristics#LENS_POSE_TRANSLATION - * @see CameraCharacteristics#LENS_RADIAL_DISTORTION - * @see CaptureRequest#SENSOR_EXPOSURE_TIME - * @see CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE - * @see CaptureResult#SENSOR_ROLLING_SHUTTER_SKEW * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES */ public static final int REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING = 10; @@ -864,19 +829,24 @@ public abstract class CameraMetadata<TKey> { * <li>{@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}</li> * </ul> * </li> + * <li>The SENSOR_INFO_TIMESTAMP_SOURCE of the logical device and physical devices must be + * the same.</li> * <li>The logical camera device must be LIMITED or higher device.</li> * </ul> * <p>Both the logical camera device and its underlying physical devices support the * mandatory stream combinations required for their device levels.</p> * <p>Additionally, for each guaranteed stream combination, the logical camera supports:</p> * <ul> - * <li>Replacing one logical {@link android.graphics.ImageFormat#YUV_420_888 YUV_420_888} + * <li>For each guaranteed stream combination, the logical camera supports replacing one + * logical {@link android.graphics.ImageFormat#YUV_420_888 YUV_420_888} * or raw stream with two physical streams of the same size and format, each from a * separate physical camera, given that the size and format are supported by both * physical cameras.</li> - * <li>Adding two raw streams, each from one physical camera, if the logical camera doesn't - * advertise RAW capability, but the underlying physical cameras do. This is usually - * the case when the physical cameras have different sensor sizes.</li> + * <li>If the logical camera doesn't advertise RAW capability, but the underlying physical + * cameras do, the logical camera will support guaranteed stream combinations for RAW + * capability, except that the RAW streams will be physical streams, each from a separate + * physical camera. This is usually the case when the physical cameras have different + * sensor sizes.</li> * </ul> * <p>Using physical streams in place of a logical stream of the same size and format will * not slow down the frame rate of the capture, as long as the minimum frame duration diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index 481b7649610a..3ed533a4efcb 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -2757,11 +2757,11 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> new Key<Integer>("android.statistics.lensShadingMapMode", int.class); /** - * <p>Whether the camera device outputs the OIS data in output + * <p>A control for selecting whether OIS position information is included in output * result metadata.</p> * <p>When set to ON, * {@link CaptureResult#STATISTICS_OIS_TIMESTAMPS android.statistics.oisTimestamps}, android.statistics.oisShiftPixelX, - * android.statistics.oisShiftPixelY will provide OIS data in the output result metadata.</p> + * and android.statistics.oisShiftPixelY provide OIS data in the output result metadata.</p> * <p><b>Possible values:</b> * <ul> * <li>{@link #STATISTICS_OIS_DATA_MODE_OFF OFF}</li> diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index d730fa8a31cf..c332d3028aed 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -3909,11 +3909,11 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { new Key<Integer>("android.statistics.lensShadingMapMode", int.class); /** - * <p>Whether the camera device outputs the OIS data in output + * <p>A control for selecting whether OIS position information is included in output * result metadata.</p> * <p>When set to ON, * {@link CaptureResult#STATISTICS_OIS_TIMESTAMPS android.statistics.oisTimestamps}, android.statistics.oisShiftPixelX, - * android.statistics.oisShiftPixelY will provide OIS data in the output result metadata.</p> + * and android.statistics.oisShiftPixelY provide OIS data in the output result metadata.</p> * <p><b>Possible values:</b> * <ul> * <li>{@link #STATISTICS_OIS_DATA_MODE_OFF OFF}</li> diff --git a/core/java/android/hardware/camera2/OWNERS b/core/java/android/hardware/camera2/OWNERS new file mode 100644 index 000000000000..18acfee14555 --- /dev/null +++ b/core/java/android/hardware/camera2/OWNERS @@ -0,0 +1,6 @@ +cychen@google.com +epeev@google.com +etalvala@google.com +shuzhenwang@google.com +yinchiayeh@google.com +zhijunhe@google.com diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java index b205e2c7649d..a040a09cf469 100644 --- a/core/java/android/hardware/camera2/params/OutputConfiguration.java +++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java @@ -82,11 +82,9 @@ import java.util.List; * * </ul> * - * <p>Please note that surface sharing is currently only enabled for outputs that use the - * {@link ImageFormat#PRIVATE} format. This includes surface sources like - * {@link android.view.SurfaceView}, {@link android.media.MediaRecorder}, - * {@link android.graphics.SurfaceTexture} and {@link android.media.ImageReader}, configured using - * the aforementioned format.</p> + * <p> As of {@link android.os.Build.VERSION_CODES#P Android P}, all formats can be used for + * sharing, subject to device support. On prior API levels, only {@link ImageFormat#PRIVATE} + * format may be used.</p> * * @see CameraDevice#createCaptureSessionByOutputConfigurations * diff --git a/core/java/android/hardware/display/AmbientBrightnessDayStats.java b/core/java/android/hardware/display/AmbientBrightnessDayStats.java index 00f3c36d0361..1aa2557f92a2 100644 --- a/core/java/android/hardware/display/AmbientBrightnessDayStats.java +++ b/core/java/android/hardware/display/AmbientBrightnessDayStats.java @@ -47,6 +47,11 @@ public final class AmbientBrightnessDayStats implements Parcelable { private final float[] mStats; /** + * Initialize day stats from the given state. The time spent in each of the bucket is + * initialized to 0. + * + * @param localDate The date for which stats are being tracked + * @param bucketBoundaries Bucket boundaries used from creating the buckets from * @hide */ public AmbientBrightnessDayStats(@NonNull LocalDate localDate, @@ -55,6 +60,11 @@ public final class AmbientBrightnessDayStats implements Parcelable { } /** + * Initialize day stats from the given state + * + * @param localDate The date for which stats are being tracked + * @param bucketBoundaries Bucket boundaries used from creating the buckets from + * @param stats Time spent in each of the buckets (in seconds) * @hide */ public AmbientBrightnessDayStats(@NonNull LocalDate localDate, @@ -81,14 +91,26 @@ public final class AmbientBrightnessDayStats implements Parcelable { mStats = stats; } + /** + * @return The {@link LocalDate} for which brightness stats are being tracked. + */ public LocalDate getLocalDate() { return mLocalDate; } + /** + * @return Aggregated stats of time spent (in seconds) in various buckets. + */ public float[] getStats() { return mStats; } + /** + * Returns the bucket boundaries (in lux) used for creating buckets. For eg., if the bucket + * boundaries array is {b1, b2, b3}, the buckets will be [b1, b2), [b2, b3), [b3, inf). + * + * @return The list of bucket boundaries. + */ public float[] getBucketBoundaries() { return mBucketBoundaries; } @@ -169,7 +191,14 @@ public final class AmbientBrightnessDayStats implements Parcelable { dest.writeFloatArray(mStats); } - /** @hide */ + /** + * Updates the stats by incrementing the time spent for the appropriate bucket based on ambient + * brightness reading. + * + * @param ambientBrightness Ambient brightness reading (in lux) + * @param durationSec Time spent with the given reading (in seconds) + * @hide + */ public void log(float ambientBrightness, float durationSec) { int bucketIndex = getBucketIndex(ambientBrightness); if (bucketIndex >= 0) { diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 92d6bbb0a20f..bd54522719b2 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -23,10 +23,12 @@ import static android.Manifest.permission.USE_FINGERPRINT; import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresFeature; import android.annotation.RequiresPermission; import android.annotation.SystemService; import android.app.ActivityManager; import android.content.Context; +import android.content.pm.PackageManager; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricFingerprintConstants; import android.os.Binder; @@ -59,6 +61,7 @@ import javax.crypto.Mac; */ @Deprecated @SystemService(Context.FINGERPRINT_SERVICE) +@RequiresFeature(PackageManager.FEATURE_FINGERPRINT) public class FingerprintManager implements BiometricFingerprintConstants { private static final String TAG = "FingerprintManager"; private static final boolean DEBUG = true; diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java index a772cbe43196..e34423c05a87 100644 --- a/core/java/android/hardware/hdmi/HdmiControlManager.java +++ b/core/java/android/hardware/hdmi/HdmiControlManager.java @@ -17,11 +17,13 @@ package android.hardware.hdmi; import android.annotation.Nullable; +import android.annotation.RequiresFeature; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SuppressLint; import android.content.Context; +import android.content.pm.PackageManager; import android.annotation.SystemApi; import android.annotation.SystemService; import android.os.RemoteException; @@ -42,6 +44,7 @@ import android.util.Log; */ @SystemApi @SystemService(Context.HDMI_CONTROL_SERVICE) +@RequiresFeature(PackageManager.FEATURE_HDMI_CEC) public final class HdmiControlManager { private static final String TAG = "HdmiControlManager"; diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java index e1d7edfa7d9c..8fde82ef2012 100644 --- a/core/java/android/hardware/radio/RadioManager.java +++ b/core/java/android/hardware/radio/RadioManager.java @@ -21,10 +21,12 @@ import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresFeature; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; import android.content.Context; +import android.content.pm.PackageManager; import android.os.Handler; import android.os.Parcel; import android.os.Parcelable; @@ -58,6 +60,7 @@ import java.util.stream.Collectors; */ @SystemApi @SystemService(Context.RADIO_SERVICE) +@RequiresFeature(PackageManager.FEATURE_BROADCAST_RADIO) public class RadioManager { private static final String TAG = "BroadcastRadio.manager"; diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java index 5b15c0d2fd9c..9e5174ad93a8 100644 --- a/core/java/android/hardware/usb/UsbDeviceConnection.java +++ b/core/java/android/hardware/usb/UsbDeviceConnection.java @@ -222,7 +222,10 @@ public class UsbDeviceConnection { * @param endpoint the endpoint for this transaction * @param buffer buffer for data to send or receive; can be {@code null} to wait for next * transaction without reading data - * @param length the length of the data to send or receive + * @param length the length of the data to send or receive. Before + * {@value Build.VERSION_CODES#P}, a value larger than 16384 bytes + * would be truncated down to 16384. In API {@value Build.VERSION_CODES#P} + * and after, any value of length is valid. * @param timeout in milliseconds, 0 is infinite * @return length of data transferred (or zero) for success, * or negative value for failure @@ -239,7 +242,10 @@ public class UsbDeviceConnection { * @param endpoint the endpoint for this transaction * @param buffer buffer for data to send or receive * @param offset the index of the first byte in the buffer to send or receive - * @param length the length of the data to send or receive + * @param length the length of the data to send or receive. Before + * {@value Build.VERSION_CODES#P}, a value larger than 16384 bytes + * would be truncated down to 16384. In API {@value Build.VERSION_CODES#P} + * and after, any value of length is valid. * @param timeout in milliseconds, 0 is infinite * @return length of data transferred (or zero) for success, * or negative value for failure @@ -247,6 +253,10 @@ public class UsbDeviceConnection { public int bulkTransfer(UsbEndpoint endpoint, byte[] buffer, int offset, int length, int timeout) { checkBounds(buffer, offset, length); + if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P + && length > UsbRequest.MAX_USBFS_BUFFER_SIZE) { + length = UsbRequest.MAX_USBFS_BUFFER_SIZE; + } return native_bulk_request(endpoint.getAddress(), buffer, offset, length, timeout); } diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java index 8daecac5a109..74a36df03b00 100644 --- a/core/java/android/hardware/usb/UsbManager.java +++ b/core/java/android/hardware/usb/UsbManager.java @@ -19,6 +19,7 @@ package android.hardware.usb; import android.Manifest; import android.annotation.Nullable; +import android.annotation.RequiresFeature; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -27,6 +28,7 @@ import android.annotation.SystemService; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; +import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.hardware.usb.gadget.V1_0.GadgetFunction; import android.os.Bundle; @@ -382,6 +384,7 @@ public class UsbManager { * * @return HashMap containing all connected USB devices. */ + @RequiresFeature(PackageManager.FEATURE_USB_HOST) public HashMap<String,UsbDevice> getDeviceList() { HashMap<String,UsbDevice> result = new HashMap<String,UsbDevice>(); if (mService == null) { @@ -406,6 +409,7 @@ public class UsbManager { * @param device the device to open * @return a {@link UsbDeviceConnection}, or {@code null} if open failed */ + @RequiresFeature(PackageManager.FEATURE_USB_HOST) public UsbDeviceConnection openDevice(UsbDevice device) { try { String deviceName = device.getDeviceName(); @@ -430,6 +434,7 @@ public class UsbManager { * * @return list of USB accessories, or null if none are attached. */ + @RequiresFeature(PackageManager.FEATURE_USB_ACCESSORY) public UsbAccessory[] getAccessoryList() { if (mService == null) { return null; @@ -452,6 +457,7 @@ public class UsbManager { * @param accessory the USB accessory to open * @return file descriptor, or null if the accessor could not be opened. */ + @RequiresFeature(PackageManager.FEATURE_USB_ACCESSORY) public ParcelFileDescriptor openAccessory(UsbAccessory accessory) { try { return mService.openAccessory(accessory); @@ -472,6 +478,7 @@ public class UsbManager { * @param device to check permissions for * @return true if caller has permission */ + @RequiresFeature(PackageManager.FEATURE_USB_HOST) public boolean hasPermission(UsbDevice device) { if (mService == null) { return false; @@ -492,6 +499,7 @@ public class UsbManager { * @param accessory to check permissions for * @return true if caller has permission */ + @RequiresFeature(PackageManager.FEATURE_USB_ACCESSORY) public boolean hasPermission(UsbAccessory accessory) { if (mService == null) { return false; @@ -525,6 +533,7 @@ public class UsbManager { * @param device to request permissions for * @param pi PendingIntent for returning result */ + @RequiresFeature(PackageManager.FEATURE_USB_HOST) public void requestPermission(UsbDevice device, PendingIntent pi) { try { mService.requestDevicePermission(device, mContext.getPackageName(), pi); @@ -551,6 +560,7 @@ public class UsbManager { * @param accessory to request permissions for * @param pi PendingIntent for returning result */ + @RequiresFeature(PackageManager.FEATURE_USB_ACCESSORY) public void requestPermission(UsbAccessory accessory, PendingIntent pi) { try { mService.requestAccessoryPermission(accessory, mContext.getPackageName(), pi); diff --git a/core/java/android/hardware/usb/UsbRequest.java b/core/java/android/hardware/usb/UsbRequest.java index 2e8f8e12b508..f59c87eecfcb 100644 --- a/core/java/android/hardware/usb/UsbRequest.java +++ b/core/java/android/hardware/usb/UsbRequest.java @@ -17,6 +17,7 @@ package android.hardware.usb; import android.annotation.Nullable; +import android.os.Build; import android.util.Log; import com.android.internal.util.Preconditions; @@ -43,7 +44,7 @@ public class UsbRequest { private static final String TAG = "UsbRequest"; // From drivers/usb/core/devio.c - private static final int MAX_USBFS_BUFFER_SIZE = 16384; + static final int MAX_USBFS_BUFFER_SIZE = 16384; // used by the JNI code private long mNativeContext; @@ -175,7 +176,9 @@ public class UsbRequest { * capacity will be ignored. Once the request * {@link UsbDeviceConnection#requestWait() is processed} the position will be set * to the number of bytes read/written. - * @param length number of bytes to read or write. + * @param length number of bytes to read or write. Before {@value Build.VERSION_CODES#P}, a + * value larger than 16384 bytes would be truncated down to 16384. In API + * {@value Build.VERSION_CODES#P} and after, any value of length is valid. * * @return true if the queueing operation succeeded * @@ -186,6 +189,11 @@ public class UsbRequest { boolean out = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT); boolean result; + if (mConnection.getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P + && length > MAX_USBFS_BUFFER_SIZE) { + length = MAX_USBFS_BUFFER_SIZE; + } + synchronized (mLock) { // save our buffer for when the request has completed mBuffer = buffer; @@ -222,7 +230,10 @@ public class UsbRequest { * of the buffer is undefined until the request is returned by * {@link UsbDeviceConnection#requestWait}. If the request failed the buffer * will be unchanged; if the request succeeded the position of the buffer is - * incremented by the number of bytes sent/received. + * incremented by the number of bytes sent/received. Before + * {@value Build.VERSION_CODES#P}, a buffer of length larger than 16384 bytes + * would throw IllegalArgumentException. In API {@value Build.VERSION_CODES#P} + * and after, any size buffer is valid. * * @return true if the queueing operation succeeded */ @@ -244,9 +255,12 @@ public class UsbRequest { mIsUsingNewQueue = true; wasQueued = native_queue(null, 0, 0); } else { - // Can only send/receive MAX_USBFS_BUFFER_SIZE bytes at once - Preconditions.checkArgumentInRange(buffer.remaining(), 0, MAX_USBFS_BUFFER_SIZE, - "number of remaining bytes"); + if (mConnection.getContext().getApplicationInfo().targetSdkVersion + < Build.VERSION_CODES.P) { + // Can only send/receive MAX_USBFS_BUFFER_SIZE bytes at once + Preconditions.checkArgumentInRange(buffer.remaining(), 0, MAX_USBFS_BUFFER_SIZE, + "number of remaining bytes"); + } // Can not receive into read-only buffers. Preconditions.checkArgument(!(buffer.isReadOnly() && !isSend), "buffer can not be " diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index a817f33ce7ba..017674f5ea8a 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -1818,9 +1818,9 @@ public class InputMethodService extends AbstractInputMethodService { } /** - * Called when the input method window has been shown to the user, after - * previously not being visible. This is done after all of the UI setup - * for the window has occurred (creating its views etc). + * Called immediately before the input method window is shown to the user. + * You could override this to prepare for the window to be shown + * (update view structure etc). */ public void onWindowShown() { // Intentionally empty diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 91ba57f334f0..fd030e24af5f 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -112,8 +112,14 @@ public class ConnectivityManager { * <p/> * For a disconnect event, the boolean extra EXTRA_NO_CONNECTIVITY * is set to {@code true} if there are no connected networks at all. + * + * @deprecated apps should use the more versatile {@link #requestNetwork}, + * {@link #registerNetworkCallback} or {@link #registerDefaultNetworkCallback} + * functions instead for faster and more detailed updates about the network + * changes they care about. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @Deprecated public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE"; /** @@ -2663,7 +2669,7 @@ public class ConnectivityManager { * A {@code NetworkCallback} is registered by calling * {@link #requestNetwork(NetworkRequest, NetworkCallback)}, * {@link #registerNetworkCallback(NetworkRequest, NetworkCallback)}, - * or {@link #registerDefaultNetworkCallback(NetworkCallback). A {@code NetworkCallback} is + * or {@link #registerDefaultNetworkCallback(NetworkCallback)}. A {@code NetworkCallback} is * unregistered by calling {@link #unregisterNetworkCallback(NetworkCallback)}. * A {@code NetworkCallback} should be registered at most once at any time. * A {@code NetworkCallback} that has been unregistered can be registered again. @@ -2692,6 +2698,32 @@ public class ConnectivityManager { * satisfying the request changes. * * @param network The {@link Network} of the satisfying network. + * @param networkCapabilities The {@link NetworkCapabilities} of the satisfying network. + * @param linkProperties The {@link LinkProperties} of the satisfying network. + * @hide + */ + public void onAvailable(Network network, NetworkCapabilities networkCapabilities, + LinkProperties linkProperties) { + // Internally only this method is called when a new network is available, and + // it calls the callback in the same way and order that older versions used + // to call so as not to change the behavior. + onAvailable(network); + if (!networkCapabilities.hasCapability( + NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)) { + onNetworkSuspended(network); + } + onCapabilitiesChanged(network, networkCapabilities); + onLinkPropertiesChanged(network, linkProperties); + } + + /** + * Called when the framework connects and has declared a new network ready for use. + * This callback may be called more than once if the {@link Network} that is + * satisfying the request changes. This will always immediately be followed by a + * call to {@link #onCapabilitiesChanged(Network, NetworkCapabilities)} then by a + * call to {@link #onLinkPropertiesChanged(Network, LinkProperties)}. + * + * @param network The {@link Network} of the satisfying network. */ public void onAvailable(Network network) {} @@ -2734,7 +2766,8 @@ public class ConnectivityManager { * changes capabilities but still satisfies the stated need. * * @param network The {@link Network} whose capabilities have changed. - * @param networkCapabilities The new {@link android.net.NetworkCapabilities} for this network. + * @param networkCapabilities The new {@link android.net.NetworkCapabilities} for this + * network. */ public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {} @@ -2750,7 +2783,7 @@ public class ConnectivityManager { /** * Called when the network the framework connected to for this request - * goes into {@link NetworkInfo.DetailedState.SUSPENDED}. + * goes into {@link NetworkInfo.State#SUSPENDED}. * This generally means that while the TCP connections are still live, * temporarily network data fails to transfer. Specifically this is used * on cellular networks to mask temporary outages when driving through @@ -2761,9 +2794,8 @@ public class ConnectivityManager { /** * Called when the network the framework connected to for this request - * returns from a {@link NetworkInfo.DetailedState.SUSPENDED} state. - * This should always be preceeded by a matching {@code onNetworkSuspended} - * call. + * returns from a {@link NetworkInfo.State#SUSPENDED} state. This should always be + * preceded by a matching {@link NetworkCallback#onNetworkSuspended} call. * @hide */ public void onNetworkResumed(Network network) {} @@ -2872,7 +2904,9 @@ public class ConnectivityManager { break; } case CALLBACK_AVAILABLE: { - callback.onAvailable(network); + NetworkCapabilities cap = getObject(message, NetworkCapabilities.class); + LinkProperties lp = getObject(message, LinkProperties.class); + callback.onAvailable(network, cap, lp); break; } case CALLBACK_LOSING: { diff --git a/core/java/android/net/IpSecConfig.java b/core/java/android/net/IpSecConfig.java index 6a262e2c87ca..8599f47c6245 100644 --- a/core/java/android/net/IpSecConfig.java +++ b/core/java/android/net/IpSecConfig.java @@ -218,6 +218,25 @@ public final class IpSecConfig implements Parcelable { @VisibleForTesting public IpSecConfig() {} + /** Copy constructor */ + @VisibleForTesting + public IpSecConfig(IpSecConfig c) { + mMode = c.mMode; + mSourceAddress = c.mSourceAddress; + mDestinationAddress = c.mDestinationAddress; + mNetwork = c.mNetwork; + mSpiResourceId = c.mSpiResourceId; + mEncryption = c.mEncryption; + mAuthentication = c.mAuthentication; + mAuthenticatedEncryption = c.mAuthenticatedEncryption; + mEncapType = c.mEncapType; + mEncapSocketResourceId = c.mEncapSocketResourceId; + mEncapRemotePort = c.mEncapRemotePort; + mNattKeepaliveInterval = c.mNattKeepaliveInterval; + mMarkValue = c.mMarkValue; + mMarkMask = c.mMarkMask; + } + private IpSecConfig(Parcel in) { mMode = in.readInt(); mSourceAddress = in.readString(); diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java index 24a078fccc1d..b60984771a2d 100644 --- a/core/java/android/net/IpSecManager.java +++ b/core/java/android/net/IpSecManager.java @@ -19,6 +19,7 @@ import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; @@ -761,6 +762,7 @@ public final class IpSecManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public IpSecTunnelInterface createIpSecTunnelInterface(@NonNull InetAddress localAddress, @NonNull InetAddress remoteAddress, @NonNull Network underlyingNetwork) throws ResourceUnavailableException, IOException { @@ -780,6 +782,7 @@ public final class IpSecManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void applyTunnelModeTransform(IpSecTunnelInterface tunnel, int direction, IpSecTransform transform) throws IOException { try { diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java index 0829b4a3e9fe..60e96f943401 100644 --- a/core/java/android/net/IpSecTransform.java +++ b/core/java/android/net/IpSecTransform.java @@ -21,6 +21,7 @@ import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.content.Context; import android.os.Binder; @@ -83,9 +84,11 @@ public final class IpSecTransform implements AutoCloseable { @Retention(RetentionPolicy.SOURCE) public @interface EncapType {} - private IpSecTransform(Context context, IpSecConfig config) { + /** @hide */ + @VisibleForTesting + public IpSecTransform(Context context, IpSecConfig config) { mContext = context; - mConfig = config; + mConfig = new IpSecConfig(config); mResourceId = INVALID_RESOURCE_ID; } @@ -142,6 +145,18 @@ public final class IpSecTransform implements AutoCloseable { } /** + * Equals method used for testing + * + * @hide + */ + @VisibleForTesting + public static boolean equals(IpSecTransform lhs, IpSecTransform rhs) { + if (lhs == null || rhs == null) return (lhs == rhs); + return IpSecConfig.equals(lhs.getConfig(), rhs.getConfig()) + && lhs.mResourceId == rhs.mResourceId; + } + + /** * Deactivate this {@code IpSecTransform} and free allocated resources. * * <p>Deactivating a transform while it is still applied to a socket will result in errors on @@ -266,6 +281,10 @@ public final class IpSecTransform implements AutoCloseable { * @hide */ @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_STACK, + android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD + }) public void startNattKeepalive(@NonNull NattKeepaliveCallback userCallback, int intervalSeconds, @NonNull Handler handler) throws IOException { checkNotNull(userCallback); @@ -305,6 +324,10 @@ public final class IpSecTransform implements AutoCloseable { * @hide */ @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_STACK, + android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD + }) public void stopNattKeepalive() { synchronized (mKeepaliveCallback) { if (mKeepalive == null) { @@ -449,6 +472,7 @@ public final class IpSecTransform implements AutoCloseable { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public IpSecTransform buildTunnelModeTransform( @NonNull InetAddress sourceAddress, @NonNull IpSecManager.SecurityParameterIndex spi) diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java index 287bdc88dd3e..74d64704c8d2 100644 --- a/core/java/android/net/MacAddress.java +++ b/core/java/android/net/MacAddress.java @@ -26,6 +26,7 @@ import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.security.SecureRandom; import java.util.Arrays; import java.util.Random; @@ -329,16 +330,34 @@ public final class MacAddress implements Parcelable { /** * Returns a generated MAC address whose 24 least significant bits constituting the - * NIC part of the address are randomly selected. + * NIC part of the address are randomly selected and has Google OUI base. * * The locally assigned bit is always set to 1. The multicast bit is always set to 0. * - * @return a random locally assigned MacAddress. + * @return a random locally assigned, unicast MacAddress with Google OUI. + * + * @hide + */ + public static @NonNull MacAddress createRandomUnicastAddressWithGoogleBase() { + return createRandomUnicastAddress(BASE_GOOGLE_MAC, new SecureRandom()); + } + + /** + * Returns a generated MAC address whose 46 bits, excluding the locally assigned bit and the + * unicast bit, are randomly selected. + * + * The locally assigned bit is always set to 1. The multicast bit is always set to 0. + * + * @return a random locally assigned, unicast MacAddress. * * @hide */ public static @NonNull MacAddress createRandomUnicastAddress() { - return createRandomUnicastAddress(BASE_GOOGLE_MAC, new Random()); + SecureRandom r = new SecureRandom(); + long addr = r.nextLong() & VALID_LONG_MASK; + addr |= LOCALLY_ASSIGNED_MASK; + addr &= ~MULTICAST_MASK; + return new MacAddress(addr); } /** @@ -355,8 +374,8 @@ public final class MacAddress implements Parcelable { */ public static @NonNull MacAddress createRandomUnicastAddress(MacAddress base, Random r) { long addr = (base.mAddr & OUI_MASK) | (NIC_MASK & r.nextLong()); - addr = addr | LOCALLY_ASSIGNED_MASK; - addr = addr & ~MULTICAST_MASK; + addr |= LOCALLY_ASSIGNED_MASK; + addr &= ~MULTICAST_MASK; return new MacAddress(addr); } diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index 8e05cfa96625..bae373d7564b 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -116,6 +116,7 @@ public final class NetworkCapabilities implements Parcelable { NET_CAPABILITY_NOT_ROAMING, NET_CAPABILITY_FOREGROUND, NET_CAPABILITY_NOT_CONGESTED, + NET_CAPABILITY_NOT_SUSPENDED, }) public @interface NetCapability { } @@ -239,7 +240,6 @@ public final class NetworkCapabilities implements Parcelable { /** * Indicates that this network is available for use by apps, and not a network that is being * kept up in the background to facilitate fast network switching. - * @hide */ public static final int NET_CAPABILITY_FOREGROUND = 19; @@ -252,8 +252,20 @@ public final class NetworkCapabilities implements Parcelable { */ public static final int NET_CAPABILITY_NOT_CONGESTED = 20; + /** + * Indicates that this network is not currently suspended. + * <p> + * When a network is suspended, the network's IP addresses and any connections + * established on the network remain valid, but the network is temporarily unable + * to transfer data. This can happen, for example, if a cellular network experiences + * a temporary loss of signal, such as when driving through a tunnel, etc. + * A network with this capability is not suspended, so is expected to be able to + * transfer data. + */ + public static final int NET_CAPABILITY_NOT_SUSPENDED = 21; + private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS; - private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_CONGESTED; + private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_SUSPENDED; /** * Network capabilities that are expected to be mutable, i.e., can change while a particular @@ -262,12 +274,13 @@ public final class NetworkCapabilities implements Parcelable { private static final long MUTABLE_CAPABILITIES = // TRUSTED can change when user explicitly connects to an untrusted network in Settings. // http://b/18206275 - (1 << NET_CAPABILITY_TRUSTED) | - (1 << NET_CAPABILITY_VALIDATED) | - (1 << NET_CAPABILITY_CAPTIVE_PORTAL) | - (1 << NET_CAPABILITY_NOT_ROAMING) | - (1 << NET_CAPABILITY_FOREGROUND) | - (1 << NET_CAPABILITY_NOT_CONGESTED); + (1 << NET_CAPABILITY_TRUSTED) + | (1 << NET_CAPABILITY_VALIDATED) + | (1 << NET_CAPABILITY_CAPTIVE_PORTAL) + | (1 << NET_CAPABILITY_NOT_ROAMING) + | (1 << NET_CAPABILITY_FOREGROUND) + | (1 << NET_CAPABILITY_NOT_CONGESTED) + | (1 << NET_CAPABILITY_NOT_SUSPENDED); /** * Network capabilities that are not allowed in NetworkRequests. This exists because the @@ -1299,6 +1312,7 @@ public final class NetworkCapabilities implements Parcelable { case NET_CAPABILITY_NOT_ROAMING: return "NOT_ROAMING"; case NET_CAPABILITY_FOREGROUND: return "FOREGROUND"; case NET_CAPABILITY_NOT_CONGESTED: return "NOT_CONGESTED"; + case NET_CAPABILITY_NOT_SUSPENDED: return "NOT_SUSPENDED"; default: return Integer.toString(capability); } } diff --git a/core/java/android/net/OWNERS b/core/java/android/net/OWNERS index 3a40cf4bf8a3..3cd37bf4fbdd 100644 --- a/core/java/android/net/OWNERS +++ b/core/java/android/net/OWNERS @@ -1,3 +1,5 @@ +set noparent + ek@google.com jsharkey@android.com jchalard@google.com diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java index 5ca3a4106a2d..437153b5e010 100644 --- a/core/java/android/net/Uri.java +++ b/core/java/android/net/Uri.java @@ -16,6 +16,7 @@ package android.net; +import android.annotation.Nullable; import android.content.Intent; import android.os.Environment; import android.os.Parcel; @@ -23,6 +24,8 @@ import android.os.Parcelable; import android.os.StrictMode; import android.util.Log; +import libcore.net.UriCodec; + import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; @@ -38,8 +41,6 @@ import java.util.Objects; import java.util.RandomAccess; import java.util.Set; -import libcore.net.UriCodec; - /** * Immutable URI reference. A URI reference includes a URI and a fragment, the * component of the URI following a '#'. Builds and parses URI references @@ -174,6 +175,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { * * @return the scheme or null if this is a relative URI */ + @Nullable public abstract String getScheme(); /** @@ -208,6 +210,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { * * @return the authority for this URI or null if not present */ + @Nullable public abstract String getAuthority(); /** @@ -219,6 +222,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { * * @return the authority for this URI or null if not present */ + @Nullable public abstract String getEncodedAuthority(); /** @@ -228,6 +232,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { * * @return the user info for this URI or null if not present */ + @Nullable public abstract String getUserInfo(); /** @@ -237,6 +242,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { * * @return the user info for this URI or null if not present */ + @Nullable public abstract String getEncodedUserInfo(); /** @@ -246,6 +252,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { * * @return the host for this URI or null if not present */ + @Nullable public abstract String getHost(); /** @@ -262,6 +269,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { * @return the decoded path, or null if this is not a hierarchical URI * (like "mailto:nobody@google.com") or the URI is invalid */ + @Nullable public abstract String getPath(); /** @@ -270,6 +278,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { * @return the encoded path, or null if this is not a hierarchical URI * (like "mailto:nobody@google.com") or the URI is invalid */ + @Nullable public abstract String getEncodedPath(); /** @@ -280,6 +289,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { * * @return the decoded query or null if there isn't one */ + @Nullable public abstract String getQuery(); /** @@ -290,6 +300,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { * * @return the encoded query or null if there isn't one */ + @Nullable public abstract String getEncodedQuery(); /** @@ -297,6 +308,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { * * @return the decoded fragment or null if there isn't one */ + @Nullable public abstract String getFragment(); /** @@ -304,6 +316,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { * * @return the encoded fragment or null if there isn't one */ + @Nullable public abstract String getEncodedFragment(); /** @@ -318,6 +331,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { * * @return the decoded last segment or null if the path is empty */ + @Nullable public abstract String getLastPathSegment(); /** @@ -1674,6 +1688,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { * @throws NullPointerException if key is null * @return the decoded value or null if no parameter is found */ + @Nullable public String getQueryParameter(String key) { if (isOpaque()) { throw new UnsupportedOperationException(NOT_HIERARCHICAL); diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java index a734719afa5d..8b4f02efd153 100644 --- a/core/java/android/os/BatteryManager.java +++ b/core/java/android/os/BatteryManager.java @@ -323,4 +323,16 @@ public class BatteryManager { public long getLongProperty(int id) { return queryProperty(id); } + + /** + * Return true if the plugType given is wired + * @param plugType {@link #BATTERY_PLUGGED_AC}, {@link #BATTERY_PLUGGED_USB}, + * or {@link #BATTERY_PLUGGED_WIRELESS} + * + * @return true if plugType is wired + * @hide + */ + public static boolean isPlugWired(int plugType) { + return plugType == BATTERY_PLUGGED_USB || plugType == BATTERY_PLUGGED_AC; + } } diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index fd0e5ae51892..7cd58e8b7c36 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -1537,6 +1537,7 @@ public abstract class BatteryStats implements Parcelable { public static final int STATE2_BLUETOOTH_ON_FLAG = 1<<22; public static final int STATE2_CAMERA_FLAG = 1<<21; public static final int STATE2_BLUETOOTH_SCAN_FLAG = 1 << 20; + public static final int STATE2_CELLULAR_HIGH_TX_POWER_FLAG = 1 << 19; public static final int MOST_INTERESTING_STATES2 = STATE2_POWER_SAVE_FLAG | STATE2_WIFI_ON_FLAG | STATE2_DEVICE_IDLE_MASK @@ -2239,12 +2240,16 @@ public abstract class BatteryStats implements Parcelable { public static final int DATA_CONNECTION_LTE = 13; public static final int DATA_CONNECTION_EHRPD = 14; public static final int DATA_CONNECTION_HSPAP = 15; - public static final int DATA_CONNECTION_OTHER = 16; + public static final int DATA_CONNECTION_GSM = 16; + public static final int DATA_CONNECTION_TD_SCDMA = 17; + public static final int DATA_CONNECTION_IWLAN = 18; + public static final int DATA_CONNECTION_LTE_CA = 19; + public static final int DATA_CONNECTION_OTHER = 20; static final String[] DATA_CONNECTION_NAMES = { "none", "gprs", "edge", "umts", "cdma", "evdo_0", "evdo_A", "1xrtt", "hsdpa", "hsupa", "hspa", "iden", "evdo_b", "lte", - "ehrpd", "hspap", "other" + "ehrpd", "hspap", "gsm", "td_scdma", "iwlan", "lte_ca", "other" }; public static final int NUM_DATA_CONNECTION_TYPES = DATA_CONNECTION_OTHER+1; @@ -2353,9 +2358,11 @@ public abstract class BatteryStats implements Parcelable { WIFI_SUPPL_STATE_NAMES, WIFI_SUPPL_STATE_SHORT_NAMES), new BitDescription(HistoryItem.STATE2_CAMERA_FLAG, "camera", "ca"), new BitDescription(HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG, "ble_scan", "bles"), + new BitDescription(HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG, + "cellular_high_tx_power", "Chtp"), new BitDescription(HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK, HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT, "gps_signal_quality", "Gss", - new String[] { "poor", "good"}, new String[] { "poor", "good"}), + new String[] { "poor", "good"}, new String[] { "poor", "good"}) }; public static final String[] HISTORY_EVENT_NAMES = new String[] { diff --git a/core/java/android/os/BestClock.java b/core/java/android/os/BestClock.java new file mode 100644 index 000000000000..aa066b633e6b --- /dev/null +++ b/core/java/android/os/BestClock.java @@ -0,0 +1,58 @@ +/* + * 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.os; + +import android.util.Log; + +import java.time.Clock; +import java.time.DateTimeException; +import java.time.ZoneId; +import java.util.Arrays; + +/** + * Single {@link Clock} that will return the best available time from a set of + * prioritized {@link Clock} instances. + * <p> + * For example, when {@link SystemClock#currentNetworkTimeClock()} isn't able to + * provide the time, this class could use {@link Clock#systemUTC()} instead. + * + * @hide + */ +public class BestClock extends SimpleClock { + private static final String TAG = "BestClock"; + + private final Clock[] clocks; + + public BestClock(ZoneId zone, Clock... clocks) { + super(zone); + this.clocks = clocks; + } + + @Override + public long millis() { + for (Clock clock : clocks) { + try { + return clock.millis(); + } catch (DateTimeException e) { + // Ignore and attempt the next clock + Log.w(TAG, e.toString()); + } + } + throw new DateTimeException( + "No clocks in " + Arrays.toString(clocks) + " were able to provide time"); + } +} diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java index 682fdb7160f4..ff7c0c6681c6 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -1028,22 +1028,33 @@ final class BinderProxy implements IBinder { * in use, then we return the same bp. * * @param nativeData C++ pointer to (possibly still empty) BinderProxyNativeData. - * Takes ownership of nativeData iff <result>.mNativeData == nativeData. Caller will usually - * delete nativeData if that's not the case. + * Takes ownership of nativeData iff <result>.mNativeData == nativeData, or if + * we exit via an exception. If neither applies, it's the callers responsibility to + * recycle nativeData. * @param iBinder C++ pointer to IBinder. Does not take ownership of referenced object. */ private static BinderProxy getInstance(long nativeData, long iBinder) { - BinderProxy result = sProxyMap.get(iBinder); - if (result == null) { + BinderProxy result; + try { + result = sProxyMap.get(iBinder); + if (result != null) { + return result; + } result = new BinderProxy(nativeData); - sProxyMap.set(iBinder, result); + } catch (Throwable e) { + // We're throwing an exception (probably OOME); don't drop nativeData. + NativeAllocationRegistry.applyFreeFunction(NoImagePreloadHolder.sNativeFinalizer, + nativeData); + throw e; } + NoImagePreloadHolder.sRegistry.registerNativeAllocation(result, nativeData); + // The registry now owns nativeData, even if registration threw an exception. + sProxyMap.set(iBinder, result); return result; } private BinderProxy(long nativeData) { mNativeData = nativeData; - NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativeData); } /** @@ -1057,8 +1068,9 @@ final class BinderProxy implements IBinder { // Use a Holder to allow static initialization of BinderProxy in the boot image, and // to avoid some initialization ordering issues. private static class NoImagePreloadHolder { + public static final long sNativeFinalizer = getNativeFinalizer(); public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry( - BinderProxy.class.getClassLoader(), getNativeFinalizer(), NATIVE_ALLOCATION_SIZE); + BinderProxy.class.getClassLoader(), sNativeFinalizer, NATIVE_ALLOCATION_SIZE); } public native boolean pingBinder(); diff --git a/core/java/android/os/ChildZygoteProcess.java b/core/java/android/os/ChildZygoteProcess.java new file mode 100644 index 000000000000..337a3e279a1a --- /dev/null +++ b/core/java/android/os/ChildZygoteProcess.java @@ -0,0 +1,44 @@ +/* + * 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.os; + +import android.net.LocalSocketAddress; + +/** + * Represents a connection to a child-zygote process. A child-zygote is spawend from another + * zygote process using {@link startChildZygote()}. + * + * {@hide} + */ +public class ChildZygoteProcess extends ZygoteProcess { + /** + * The PID of the child zygote process. + */ + private final int mPid; + + ChildZygoteProcess(LocalSocketAddress socketAddress, int pid) { + super(socketAddress, null); + mPid = pid; + } + + /** + * Returns the PID of the child-zygote process. + */ + public int getPid() { + return mPid; + } +} diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java index cdee1101c27b..228fe7a3dae5 100644 --- a/core/java/android/os/HwBinder.java +++ b/core/java/android/os/HwBinder.java @@ -29,7 +29,13 @@ public abstract class HwBinder implements IHwBinder { private static final NativeAllocationRegistry sNativeRegistry; - /** @hide */ + /** + * Create and initialize a HwBinder object and the native objects + * used to allow this to participate in hwbinder transactions. + * + * @hide + */ + @SystemApi public HwBinder() { native_setup(); @@ -44,12 +50,28 @@ public abstract class HwBinder implements IHwBinder { int code, HwParcel request, HwParcel reply, int flags) throws RemoteException; - /** @hide */ + /** + * Process a hwbinder transaction. + * + * @param code interface specific code for interface. + * @param request parceled transaction + * @param reply object to parcel reply into + * @param flags transaction flags to be chosen by wire protocol + * + * @hide + */ + @SystemApi public abstract void onTransact( int code, HwParcel request, HwParcel reply, int flags) throws RemoteException; - /** @hide */ + /** + * Registers this service with the hwservicemanager. + * + * @param serviceName instance name of the service + * @hide + */ + @SystemApi public native final void registerService(String serviceName) throws RemoteException; diff --git a/core/java/android/os/IHwBinder.java b/core/java/android/os/IHwBinder.java index a565dee5ddd0..fbdf27e38d67 100644 --- a/core/java/android/os/IHwBinder.java +++ b/core/java/android/os/IHwBinder.java @@ -21,12 +21,6 @@ import android.annotation.SystemApi; /** @hide */ @SystemApi public interface IHwBinder { - // These MUST match their corresponding libhwbinder/IBinder.h definition !!! - /** @hide */ - public static final int FIRST_CALL_TRANSACTION = 1; - /** @hide */ - public static final int FLAG_ONEWAY = 1; - /** * Process a hwbinder transaction. * diff --git a/core/java/android/os/IStatsCompanionService.aidl b/core/java/android/os/IStatsCompanionService.aidl index 8a27700edc15..eae52171ee48 100644 --- a/core/java/android/os/IStatsCompanionService.aidl +++ b/core/java/android/os/IStatsCompanionService.aidl @@ -55,9 +55,8 @@ interface IStatsCompanionService { /** Pull the specified data. Results will be sent to statsd when complete. */ StatsLogEventWrapper[] pullData(int pullCode); - /** Send a broadcast to the specified pkg and class that it should getData now. */ - // TODO: Rename this and use a pending intent instead. - oneway void sendBroadcast(String pkg, String cls); + /** Send a broadcast to the specified PendingIntent's as IBinder that it should getData now. */ + oneway void sendDataBroadcast(in IBinder intentSender); /** * Requests StatsCompanionService to send a broadcast using the given intentSender diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl index 679b49dfb974..682a24f17648 100644 --- a/core/java/android/os/IStatsManager.aidl +++ b/core/java/android/os/IStatsManager.aidl @@ -81,12 +81,26 @@ interface IStatsManager { /** * Sets a configuration with the specified config key and subscribes to updates for this * configuration key. Broadcasts will be sent if this configuration needs to be collected. - * The configuration must be a wire-encoded StatsdConfig. The caller specifies the name of the - * package and class that should receive these broadcasts. + * The configuration must be a wire-encoded StatsDConfig. The receiver for this data is + * registered in a separate function. * * Returns if this configuration was correctly registered. */ - boolean addConfiguration(in long configKey, in byte[] config, in String pkg, in String cls); + boolean addConfiguration(in long configKey, in byte[] config); + + /** + * Registers the given pending intent for this config key. This intent is invoked when the + * memory consumed by the metrics for this configuration approach the pre-defined limits. There + * can be at most one listener per config key. + * + * Returns if this listener was correctly registered. + */ + boolean setDataFetchOperation(long configKey, in IBinder intentSender); + + /** + * Removes the data fetch operation for the specified configuration. + */ + boolean removeDataFetchOperation(long configKey); /** * Removes the configuration with the matching config key. No-op if this config key does not diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index 65e9473380ce..5e23932c48cc 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -71,6 +71,7 @@ interface IUserManager { Bundle getUserRestrictions(int userHandle); boolean hasBaseUserRestriction(String restrictionKey, int userHandle); boolean hasUserRestriction(in String restrictionKey, int userHandle); + boolean hasUserRestrictionOnAnyUser(in String restrictionKey); void setUserRestriction(String key, boolean value, int userHandle); void setApplicationRestrictions(in String packageName, in Bundle restrictions, int userHandle); diff --git a/core/java/android/os/SimpleClock.java b/core/java/android/os/SimpleClock.java new file mode 100644 index 000000000000..efc271f5408f --- /dev/null +++ b/core/java/android/os/SimpleClock.java @@ -0,0 +1,53 @@ +/* + * 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.os; + +import java.time.Clock; +import java.time.Instant; +import java.time.ZoneId; + +/** {@hide} */ +public abstract class SimpleClock extends Clock { + private final ZoneId zone; + + public SimpleClock(ZoneId zone) { + this.zone = zone; + } + + @Override + public ZoneId getZone() { + return zone; + } + + @Override + public Clock withZone(ZoneId zone) { + return new SimpleClock(zone) { + @Override + public long millis() { + return SimpleClock.this.millis(); + } + }; + } + + @Override + public abstract long millis(); + + @Override + public Instant instant() { + return Instant.ofEpochMilli(millis()); + } +} diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java index c52c22d60ade..0f70427e6dc0 100644 --- a/core/java/android/os/SystemClock.java +++ b/core/java/android/os/SystemClock.java @@ -24,8 +24,7 @@ import android.util.Slog; import dalvik.annotation.optimization.CriticalNative; import java.time.Clock; -import java.time.Instant; -import java.time.ZoneId; +import java.time.DateTimeException; import java.time.ZoneOffset; /** @@ -148,8 +147,8 @@ public final class SystemClock { * @return if the clock was successfully set to the specified time. */ public static boolean setCurrentTimeMillis(long millis) { - IBinder b = ServiceManager.getService(Context.ALARM_SERVICE); - IAlarmManager mgr = IAlarmManager.Stub.asInterface(b); + final IAlarmManager mgr = IAlarmManager.Stub + .asInterface(ServiceManager.getService(Context.ALARM_SERVICE)); if (mgr == null) { return false; } @@ -174,27 +173,25 @@ public final class SystemClock { native public static long uptimeMillis(); /** + * @removed + */ + @Deprecated + public static @NonNull Clock uptimeMillisClock() { + return uptimeClock(); + } + + /** * Return {@link Clock} that starts at system boot, not counting time spent * in deep sleep. + * + * @removed */ - public static @NonNull Clock uptimeMillisClock() { - return new Clock() { - @Override - public ZoneId getZone() { - return ZoneOffset.UTC; - } - @Override - public Clock withZone(ZoneId zone) { - throw new UnsupportedOperationException(); - } + public static @NonNull Clock uptimeClock() { + return new SimpleClock(ZoneOffset.UTC) { @Override public long millis() { return SystemClock.uptimeMillis(); } - @Override - public Instant instant() { - return Instant.ofEpochMilli(millis()); - } }; } @@ -209,25 +206,15 @@ public final class SystemClock { /** * Return {@link Clock} that starts at system boot, including time spent in * sleep. + * + * @removed */ public static @NonNull Clock elapsedRealtimeClock() { - return new Clock() { - @Override - public ZoneId getZone() { - return ZoneOffset.UTC; - } - @Override - public Clock withZone(ZoneId zone) { - throw new UnsupportedOperationException(); - } + return new SimpleClock(ZoneOffset.UTC) { @Override public long millis() { return SystemClock.elapsedRealtime(); } - @Override - public Instant instant() { - return Instant.ofEpochMilli(millis()); - } }; } @@ -266,4 +253,62 @@ public final class SystemClock { */ @CriticalNative public static native long currentTimeMicro(); + + /** + * Returns milliseconds since January 1, 1970 00:00:00.0 UTC, synchronized + * using a remote network source outside the device. + * <p> + * While the time returned by {@link System#currentTimeMillis()} can be + * adjusted by the user, the time returned by this method cannot be adjusted + * by the user. Note that synchronization may occur using an insecure + * network protocol, so the returned time should not be used for security + * purposes. + * <p> + * This performs no blocking network operations and returns values based on + * a recent successful synchronization event; it will either return a valid + * time or throw. + * + * @throws DateTimeException when no accurate network time can be provided. + */ + public static long currentNetworkTimeMillis() { + final IAlarmManager mgr = IAlarmManager.Stub + .asInterface(ServiceManager.getService(Context.ALARM_SERVICE)); + if (mgr != null) { + try { + return mgr.currentNetworkTimeMillis(); + } catch (ParcelableException e) { + e.maybeRethrow(DateTimeException.class); + throw new RuntimeException(e); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } else { + throw new RuntimeException(new DeadSystemException()); + } + } + + /** + * Returns a {@link Clock} that starts at January 1, 1970 00:00:00.0 UTC, + * synchronized using a remote network source outside the device. + * <p> + * While the time returned by {@link System#currentTimeMillis()} can be + * adjusted by the user, the time returned by this method cannot be adjusted + * by the user. Note that synchronization may occur using an insecure + * network protocol, so the returned time should not be used for security + * purposes. + * <p> + * This performs no blocking network operations and returns values based on + * a recent successful synchronization event; it will either return a valid + * time or throw. + * + * @throws DateTimeException when no accurate network time can be provided. + */ + public static @NonNull Clock currentNetworkTimeClock() { + return new SimpleClock(ZoneOffset.UTC) { + @Override + public long millis() { + return SystemClock.currentNetworkTimeMillis(); + } + }; + } } diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java index c6149bed9d6d..24c9c9177360 100644 --- a/core/java/android/os/UpdateEngine.java +++ b/core/java/android/os/UpdateEngine.java @@ -271,4 +271,22 @@ public class UpdateEngine { } } } + + /** + * Verifies that a payload associated with the given payload metadata + * {@code payloadMetadataFilename} can be safely applied to ths device. + * Returns {@code true} if the update can successfully be applied and + * returns {@code false} otherwise. + * + * @param payloadMetadataFilename the location of the metadata without the + * {@code file://} prefix. + */ + @SystemApi + public boolean verifyPayloadMetadata(String payloadMetadataFilename) { + try { + return mUpdateEngine.verifyPayloadApplicable(payloadMetadataFilename); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 2093cec769da..185620066454 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -943,6 +943,20 @@ public class UserManager { * @see #getUserRestrictions() */ public static final String DISALLOW_SHARE_INTO_MANAGED_PROFILE = "no_sharing_into_profile"; + + /** + * Specifies whether the user is allowed to print. + * + * This restriction can be set by device or profile owner. + * + * The default value is {@code false}. + * + * @see DevicePolicyManager#addUserRestriction(ComponentName, String) + * @see DevicePolicyManager#clearUserRestriction(ComponentName, String) + * @see #getUserRestrictions() + */ + public static final String DISALLOW_PRINTING = "no_printing"; + /** * Application restriction key that is used to indicate the pending arrival * of real restrictions for the app. @@ -1664,6 +1678,18 @@ public class UserManager { } /** + * @hide + * Returns whether any user on the device has the given user restriction set. + */ + public boolean hasUserRestrictionOnAnyUser(String restrictionKey) { + try { + return mService.hasUserRestrictionOnAnyUser(restrictionKey); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** * Return the serial number for a user. This is a device-unique * number assigned to that user; if the user is deleted and then a new * user created, the new users will not be given the same serial number. diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java index b6f16a7b9ff8..e9b48535a34e 100644 --- a/core/java/android/os/VibrationEffect.java +++ b/core/java/android/os/VibrationEffect.java @@ -16,10 +16,15 @@ package android.os; -import android.hardware.vibrator.V1_0.Constants.EffectStrength; -import android.hardware.vibrator.V1_1.Constants.Effect_1_1; +import android.annotation.Nullable; +import android.content.Context; +import android.hardware.vibrator.V1_0.EffectStrength; +import android.hardware.vibrator.V1_2.Effect; +import android.net.Uri; import android.util.MathUtils; +import com.android.internal.annotations.VisibleForTesting; + import java.util.Arrays; /** @@ -49,7 +54,7 @@ public abstract class VibrationEffect implements Parcelable { * @see #get(int) * @hide */ - public static final int EFFECT_CLICK = Effect_1_1.CLICK; + public static final int EFFECT_CLICK = Effect.CLICK; /** * A double click effect. @@ -57,14 +62,62 @@ public abstract class VibrationEffect implements Parcelable { * @see #get(int) * @hide */ - public static final int EFFECT_DOUBLE_CLICK = Effect_1_1.DOUBLE_CLICK; + public static final int EFFECT_DOUBLE_CLICK = Effect.DOUBLE_CLICK; /** * A tick effect. * @see #get(int) * @hide */ - public static final int EFFECT_TICK = Effect_1_1.TICK; + public static final int EFFECT_TICK = Effect.TICK; + + /** + * A thud effect. + * @see #get(int) + * @hide + */ + public static final int EFFECT_THUD = Effect.THUD; + + /** + * A pop effect. + * @see #get(int) + * @hide + */ + public static final int EFFECT_POP = Effect.POP; + + /** + * A heavy click effect. + * @see #get(int) + * @hide + */ + public static final int EFFECT_HEAVY_CLICK = Effect.HEAVY_CLICK; + + + /** + * Ringtone patterns. They may correspond with the device's ringtone audio, or may just be a + * pattern that can be played as a ringtone with any audio, depending on the device. + * + * @see #get(Uri, Context) + * @hide + */ + @VisibleForTesting + public static final int[] RINGTONES = { + Effect.RINGTONE_1, + Effect.RINGTONE_2, + Effect.RINGTONE_3, + Effect.RINGTONE_4, + Effect.RINGTONE_5, + Effect.RINGTONE_6, + Effect.RINGTONE_7, + Effect.RINGTONE_8, + Effect.RINGTONE_9, + Effect.RINGTONE_10, + Effect.RINGTONE_11, + Effect.RINGTONE_12, + Effect.RINGTONE_13, + Effect.RINGTONE_14, + Effect.RINGTONE_15 + }; /** @hide to prevent subclassing from outside of the framework */ public VibrationEffect() { } @@ -198,6 +251,37 @@ public abstract class VibrationEffect implements Parcelable { return effect; } + /** + * Get a predefined vibration effect associated with a given URI. + * + * Predefined effects are a set of common vibration effects that should be identical, regardless + * of the app they come from, in order to provide a cohesive experience for users across + * the entire device. They also may be custom tailored to the device hardware in order to + * provide a better experience than you could otherwise build using the generic building + * blocks. + * + * @param uri The URI associated with the haptic effect. + * @param context The context used to get the URI to haptic effect association. + * + * @return The desired effect, or {@code null} if there's no associated effect. + * + * @hide + */ + @Nullable + public static VibrationEffect get(Uri uri, Context context) { + String[] uris = context.getResources().getStringArray( + com.android.internal.R.array.config_ringtoneEffectUris); + for (int i = 0; i < uris.length && i < RINGTONES.length; i++) { + if (uris[i] == null) { + continue; + } + if (Uri.parse(uris[i]).equals(uri)) { + return get(RINGTONES[i]); + } + } + return null; + } + @Override public int describeContents() { return 0; @@ -548,10 +632,15 @@ public abstract class VibrationEffect implements Parcelable { case EFFECT_CLICK: case EFFECT_DOUBLE_CLICK: case EFFECT_TICK: + case EFFECT_THUD: + case EFFECT_POP: + case EFFECT_HEAVY_CLICK: break; default: - throw new IllegalArgumentException( - "Unknown prebaked effect type (value=" + mEffectId + ")"); + if (mEffectId < RINGTONES[0] || mEffectId > RINGTONES[RINGTONES.length - 1]) { + throw new IllegalArgumentException( + "Unknown prebaked effect type (value=" + mEffectId + ")"); + } } if (!isValidEffectStrength(mEffectStrength)) { throw new IllegalArgumentException( diff --git a/core/java/android/os/VintfObject.java b/core/java/android/os/VintfObject.java index 12a495bf2821..fb22194098b6 100644 --- a/core/java/android/os/VintfObject.java +++ b/core/java/android/os/VintfObject.java @@ -80,4 +80,11 @@ public class VintfObject { * ("28", ["libjpeg.so", "libbase.so"])] */ public static native Map<String, String[]> getVndkSnapshots(); + + /** + * @return target FCM version, a number specified in the device manifest + * indicating the FCM version that the device manifest implements. Null if + * device manifest doesn't specify this number (for legacy devices). + */ + public static native Long getTargetFrameworkCompatibilityMatrixVersion(); } diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java index 670f7949dd19..57418c8b9879 100644 --- a/core/java/android/os/ZygoteProcess.java +++ b/core/java/android/os/ZygoteProcess.java @@ -33,6 +33,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.UUID; /*package*/ class ZygoteStartFailedEx extends Exception { ZygoteStartFailedEx(String s) { @@ -61,18 +62,27 @@ public class ZygoteProcess { /** * The name of the socket used to communicate with the primary zygote. */ - private final String mSocket; + private final LocalSocketAddress mSocket; /** * The name of the secondary (alternate ABI) zygote socket. */ - private final String mSecondarySocket; + private final LocalSocketAddress mSecondarySocket; public ZygoteProcess(String primarySocket, String secondarySocket) { + this(new LocalSocketAddress(primarySocket, LocalSocketAddress.Namespace.RESERVED), + new LocalSocketAddress(secondarySocket, LocalSocketAddress.Namespace.RESERVED)); + } + + public ZygoteProcess(LocalSocketAddress primarySocket, LocalSocketAddress secondarySocket) { mSocket = primarySocket; mSecondarySocket = secondarySocket; } + public LocalSocketAddress getPrimarySocketAddress() { + return mSocket; + } + /** * State for communicating with the zygote process. */ @@ -92,14 +102,13 @@ public class ZygoteProcess { this.abiList = abiList; } - public static ZygoteState connect(String socketAddress) throws IOException { + public static ZygoteState connect(LocalSocketAddress address) throws IOException { DataInputStream zygoteInputStream = null; BufferedWriter zygoteWriter = null; final LocalSocket zygoteSocket = new LocalSocket(); try { - zygoteSocket.connect(new LocalSocketAddress(socketAddress, - LocalSocketAddress.Namespace.RESERVED)); + zygoteSocket.connect(address); zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream()); @@ -115,8 +124,8 @@ public class ZygoteProcess { } String abiListString = getAbiList(zygoteWriter, zygoteInputStream); - Log.i("Zygote", "Process: zygote socket " + socketAddress + " opened, supported ABIS: " - + abiListString); + Log.i("Zygote", "Process: zygote socket " + address.getNamespace() + "/" + + address.getName() + " opened, supported ABIS: " + abiListString); return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter, Arrays.asList(abiListString.split(","))); @@ -209,7 +218,8 @@ public class ZygoteProcess { try { return startViaZygote(processClass, niceName, uid, gid, gids, runtimeFlags, mountExternal, targetSdkVersion, seInfo, - abi, instructionSet, appDataDir, invokeWith, zygoteArgs); + abi, instructionSet, appDataDir, invokeWith, false /* startChildZygote */, + zygoteArgs); } catch (ZygoteStartFailedEx ex) { Log.e(LOG_TAG, "Starting VM process through Zygote failed"); @@ -325,6 +335,8 @@ public class ZygoteProcess { * @param abi the ABI the process should use. * @param instructionSet null-ok the instruction set to use. * @param appDataDir null-ok the data directory of the app. + * @param startChildZygote Start a sub-zygote. This creates a new zygote process + * that has its state cloned from this zygote process. * @param extraArgs Additional arguments to supply to the zygote process. * @return An object that describes the result of the attempt to start the process. * @throws ZygoteStartFailedEx if process start failed for any reason @@ -340,6 +352,7 @@ public class ZygoteProcess { String instructionSet, String appDataDir, String invokeWith, + boolean startChildZygote, String[] extraArgs) throws ZygoteStartFailedEx { ArrayList<String> argsForZygote = new ArrayList<String>(); @@ -396,6 +409,10 @@ public class ZygoteProcess { argsForZygote.add(invokeWith); } + if (startChildZygote) { + argsForZygote.add("--start-child-zygote"); + } + argsForZygote.add(processClass); if (extraArgs != null) { @@ -410,6 +427,18 @@ public class ZygoteProcess { } /** + * Closes the connections to the zygote, if they exist. + */ + public void close() { + if (primaryZygoteState != null) { + primaryZygoteState.close(); + } + if (secondaryZygoteState != null) { + secondaryZygoteState.close(); + } + } + + /** * Tries to establish a connection to the zygote that handles a given {@code abi}. Might block * and retry if the zygote is unresponsive. This method is a no-op if a connection is * already open. @@ -514,9 +543,19 @@ public class ZygoteProcess { * @param socketName The name of the socket to connect to. */ public static void waitForConnectionToZygote(String socketName) { + final LocalSocketAddress address = + new LocalSocketAddress(socketName, LocalSocketAddress.Namespace.RESERVED); + waitForConnectionToZygote(address); + } + + /** + * Try connecting to the Zygote over and over again until we hit a time-out. + * @param address The name of the socket to connect to. + */ + public static void waitForConnectionToZygote(LocalSocketAddress address) { for (int n = 20; n >= 0; n--) { try { - final ZygoteState zs = ZygoteState.connect(socketName); + final ZygoteState zs = ZygoteState.connect(address); zs.close(); return; } catch (IOException ioe) { @@ -529,6 +568,38 @@ public class ZygoteProcess { } catch (InterruptedException ie) { } } - Slog.wtf(LOG_TAG, "Failed to connect to Zygote through socket " + socketName); + Slog.wtf(LOG_TAG, "Failed to connect to Zygote through socket " + address.getName()); + } + + /** + * Starts a new zygote process as a child of this zygote. This is used to create + * secondary zygotes that inherit data from the zygote that this object + * communicates with. This returns a new ZygoteProcess representing a connection + * to the newly created zygote. Throws an exception if the zygote cannot be started. + */ + public ChildZygoteProcess startChildZygote(final String processClass, + final String niceName, + int uid, int gid, int[] gids, + int runtimeFlags, + String seInfo, + String abi, + String instructionSet) { + // Create an unguessable address in the global abstract namespace. + final LocalSocketAddress serverAddress = new LocalSocketAddress( + processClass + "/" + UUID.randomUUID().toString()); + + final String[] extraArgs = {Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG + serverAddress.getName()}; + + Process.ProcessStartResult result; + try { + result = startViaZygote(processClass, niceName, uid, gid, + gids, runtimeFlags, 0 /* mountExternal */, 0 /* targetSdkVersion */, seInfo, + abi, instructionSet, null /* appDataDir */, null /* invokeWith */, + true /* startChildZygote */, extraArgs); + } catch (ZygoteStartFailedEx ex) { + throw new RuntimeException("Starting child-zygote through Zygote failed", ex); + } + + return new ChildZygoteProcess(serverAddress, result.pid); } } diff --git a/core/java/android/os/storage/StorageResultCode.java b/core/java/android/os/storage/StorageResultCode.java deleted file mode 100644 index c8438870585a..000000000000 --- a/core/java/android/os/storage/StorageResultCode.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 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.os.storage; - -/** - * Class that provides access to constants returned from StorageManager - * and lower level StorageManagerService APIs. - * - * @hide - */ -public class StorageResultCode -{ - /** - * Operation succeeded. - * @see android.os.storage.StorageManager - */ - public static final int OperationSucceeded = 0; - - /** - * Operation failed: Internal error. - * @see android.os.storage.StorageManager - */ - public static final int OperationFailedInternalError = -1; - - /** - * Operation failed: Missing media. - * @see android.os.storage.StorageManager - */ - public static final int OperationFailedNoMedia = -2; - - /** - * Operation failed: Media is blank. - * @see android.os.storage.StorageManager - */ - public static final int OperationFailedMediaBlank = -3; - - /** - * Operation failed: Media is corrupt. - * @see android.os.storage.StorageManager - */ - public static final int OperationFailedMediaCorrupt = -4; - - /** - * Operation failed: Storage not mounted. - * @see android.os.storage.StorageManager - */ - public static final int OperationFailedStorageNotMounted = -5; - - /** - * Operation failed: Storage is mounted. - * @see android.os.storage.StorageManager - */ - public static final int OperationFailedStorageMounted = -6; - - /** - * Operation failed: Storage is busy. - * @see android.os.storage.StorageManager - */ - public static final int OperationFailedStorageBusy = -7; - -} diff --git a/core/java/android/preference/OWNERS b/core/java/android/preference/OWNERS new file mode 100644 index 000000000000..d20511fcfdf2 --- /dev/null +++ b/core/java/android/preference/OWNERS @@ -0,0 +1,2 @@ +pavlis@google.com +clarabayarri@google.com diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java index 51b77980fcf4..e436bc6ea30f 100644 --- a/core/java/android/print/PrintManager.java +++ b/core/java/android/print/PrintManager.java @@ -18,6 +18,7 @@ package android.print; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresFeature; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; @@ -27,6 +28,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.IntentSender; import android.content.IntentSender.SendIntentException; +import android.content.pm.PackageManager; import android.graphics.drawable.Icon; import android.os.Bundle; import android.os.CancellationSignal; @@ -104,6 +106,7 @@ import java.util.Map; * @see PrintJobInfo */ @SystemService(Context.PRINT_SERVICE) +@RequiresFeature(PackageManager.FEATURE_PRINTING) public final class PrintManager { private static final String LOG_TAG = "PrintManager"; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index e709c532200b..2ec9009ee549 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3692,18 +3692,15 @@ public final class Settings { new SettingsValidators.InclusiveIntegerRangeValidator(0, 3); /** - * User-selected RTT mode + * User-selected RTT mode. When on, outgoing and incoming calls will be answered as RTT + * calls when supported by the device and carrier. Boolean value. * 0 = OFF - * 1 = FULL - * 2 = VCO - * 3 = HCO - * Uses the same constants as TTY (e.g. {@link android.telecom.TelecomManager#TTY_MODE_OFF}) - * @hide + * 1 = ON */ public static final String RTT_CALLING_MODE = "rtt_calling_mode"; /** @hide */ - public static final Validator RTT_CALLING_MODE_VALIDATOR = TTY_MODE_VALIDATOR; + public static final Validator RTT_CALLING_MODE_VALIDATOR = BOOLEAN_VALIDATOR; /** * Whether the sounds effects (key clicks, lid open ...) are enabled. The value is @@ -5385,6 +5382,17 @@ public final class Settings { "autofill_user_data_max_field_classification_size"; /** + * Defines value returned by + * {@link android.service.autofill.UserData#getMaxCategoryCount()}. + * + * @hide + */ + @SystemApi + @TestApi + public static final String AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT = + "autofill_user_data_max_category_count"; + + /** * Defines value returned by {@link android.service.autofill.UserData#getMaxValueLength()}. * * @hide @@ -5443,32 +5451,6 @@ public final class Settings { */ public static final String ENABLED_INPUT_METHODS = "enabled_input_methods"; - private static final Validator ENABLED_INPUT_METHODS_VALIDATOR = new Validator() { - @Override - public boolean validate(String value) { - if (value == null) { - return false; - } - String[] inputMethods = value.split(":"); - boolean valid = true; - for (String inputMethod : inputMethods) { - if (inputMethod.length() == 0) { - return false; - } - String[] subparts = inputMethod.split(";"); - for (String subpart : subparts) { - // allow either a non negative integer or a ComponentName - valid |= (NON_NEGATIVE_INTEGER_VALIDATOR.validate(subpart) - || COMPONENT_NAME_VALIDATOR.validate(subpart)); - } - if (!valid) { - return false; - } - } - return valid; - } - }; - /** * List of system input methods that are currently disabled. This is a string * containing the IDs of all disabled input methods, each ID separated @@ -7396,7 +7378,8 @@ public final class Settings { */ public static final String NIGHT_DISPLAY_AUTO_MODE = "night_display_auto_mode"; - private static final Validator NIGHT_DISPLAY_AUTO_MODE_VALIDATOR = BOOLEAN_VALIDATOR; + private static final Validator NIGHT_DISPLAY_AUTO_MODE_VALIDATOR = + new SettingsValidators.InclusiveIntegerRangeValidator(0, 2); /** * Control the color temperature of Night Display, represented in Kelvin. @@ -7679,6 +7662,24 @@ public final class Settings { */ public static final String BACKUP_MANAGER_CONSTANTS = "backup_manager_constants"; + + /** + * Local transport parameters so we can configure it for tests. + * This is encoded as a key=value list, separated by commas. + * + * The following keys are supported: + * + * <pre> + * fake_encryption_flag (boolean) + * </pre> + * + * <p> + * Type: string + * @hide + */ + public static final String BACKUP_LOCAL_TRANSPORT_PARAMETERS = + "backup_local_transport_parameters"; + /** * Flag to set if the system should predictively attempt to re-enable Bluetooth while * the user is driving. @@ -7708,7 +7709,6 @@ public final class Settings { ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, ENABLED_ACCESSIBILITY_SERVICES, ENABLED_VR_LISTENERS, - ENABLED_INPUT_METHODS, TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, TOUCH_EXPLORATION_ENABLED, ACCESSIBILITY_ENABLED, @@ -7814,7 +7814,6 @@ public final class Settings { VALIDATORS.put(ENABLED_ACCESSIBILITY_SERVICES, ENABLED_ACCESSIBILITY_SERVICES_VALIDATOR); VALIDATORS.put(ENABLED_VR_LISTENERS, ENABLED_VR_LISTENERS_VALIDATOR); - VALIDATORS.put(ENABLED_INPUT_METHODS, ENABLED_INPUT_METHODS_VALIDATOR); VALIDATORS.put(TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES_VALIDATOR); VALIDATORS.put(TOUCH_EXPLORATION_ENABLED, TOUCH_EXPLORATION_ENABLED_VALIDATOR); @@ -8389,10 +8388,10 @@ public final class Settings { private static final Validator POWER_SOUNDS_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; /** - * URI for the "wireless charging started" sound. + * URI for the "wireless charging started" and "wired charging started" sound. * @hide */ - public static final String WIRELESS_CHARGING_STARTED_SOUND = + public static final String CHARGING_STARTED_SOUND = "wireless_charging_started_sound"; /** @@ -8562,9 +8561,8 @@ public final class Settings { * * @see android.service.euicc.EuiccService * @hide - * - * TODO(b/35851809): Make this a SystemApi. */ + @SystemApi public static final String DEFAULT_SM_DP_PLUS = "default_sm_dp_plus"; /** @@ -8780,6 +8778,7 @@ public final class Settings { /** {@hide} */ public static final String NETSTATS_POLL_INTERVAL = "netstats_poll_interval"; /** {@hide} */ + @Deprecated public static final String NETSTATS_TIME_CACHE_MAX_AGE = "netstats_time_cache_max_age"; /** {@hide} */ public static final String NETSTATS_GLOBAL_ALERT_BYTES = "netstats_global_alert_bytes"; @@ -10477,6 +10476,9 @@ public final class Settings { * <pre> * smart_selection_dark_launch (boolean) * smart_selection_enabled_for_edit_text (boolean) + * suggest_selection_max_range_length (int) + * classify_text_max_range_length (int) + * generate_links_max_text_length (int) * </pre> * * <p> @@ -10495,6 +10497,7 @@ public final class Settings { * track_cpu_times_by_proc_state (boolean) * track_cpu_active_cluster_time (boolean) * read_binary_cpu_time (boolean) + * proc_state_cpu_times_read_delay_ms (long) * </pre> * * <p> @@ -10505,6 +10508,16 @@ public final class Settings { public static final String BATTERY_STATS_CONSTANTS = "battery_stats_constants"; /** + * SyncManager specific settings. + * + * <p> + * Type: string + * @hide + * @see com.android.server.content.SyncManagerConstants + */ + public static final String SYNC_MANAGER_CONSTANTS = "sync_manager_constants"; + + /** * Whether or not App Standby feature is enabled. This controls throttling of apps * based on usage patterns and predictions. * Type: int (0 for false, 1 for true) @@ -11372,15 +11385,25 @@ public final class Settings { "chained_battery_attribution_enabled"; /** - * The packages whitelisted to be run in autofill compatibility mode. + * The packages whitelisted to be run in autofill compatibility mode. The list + * of packages is ":" colon delimited. * * @hide */ @SystemApi + @TestApi public static final String AUTOFILL_COMPAT_ALLOWED_PACKAGES = "autofill_compat_allowed_packages"; /** + * Exemptions to the hidden API blacklist. + * + * @hide + */ + public static final String HIDDEN_API_BLACKLIST_EXEMPTIONS = + "hidden_api_blacklist_exemptions"; + + /** * Settings to backup. This is here so that it's in the same place as the settings * keys and easy to update. * @@ -12079,6 +12102,28 @@ public final class Settings { "enable_gnss_raw_meas_full_tracking"; /** + * Whether the notification should be ongoing (persistent) when a carrier app install is + * required. + * + * The value is a boolean (1 or 0). + * @hide + */ + @SystemApi + public static final String INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT = + "install_carrier_app_notification_persistent"; + + /** + * The amount of time (ms) to hide the install carrier app notification after the user has + * ignored it. After this time passes, the notification will be shown again + * + * The value is a long + * @hide + */ + @SystemApi + public static final String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS = + "install_carrier_app_notification_sleep_millis"; + + /** * Whether we've enabled zram on this device. Takes effect on * reboot. The value "1" enables zram; "0" disables it, and * everything else is unspecified. @@ -12088,11 +12133,22 @@ public final class Settings { "zram_enabled"; /** - * Whether smart replies in notifications are enabled. + * Configuration flags for smart replies in notifications. + * This is encoded as a key=value list, separated by commas. Ex: + * + * "enabled=1,max_squeeze_remeasure_count=3" + * + * The following keys are supported: + * + * <pre> + * enabled (boolean) + * max_squeeze_remeasure_attempts (int) + * </pre> + * @see com.android.systemui.statusbar.policy.SmartReplyConstants * @hide */ - public static final String ENABLE_SMART_REPLIES_IN_NOTIFICATIONS = - "enable_smart_replies_in_notifications"; + public static final String SMART_REPLIES_IN_NOTIFICATIONS_FLAGS = + "smart_replies_in_notifications_flags"; /** * If nonzero, crashes in foreground processes will bring up a dialog. @@ -12113,6 +12169,12 @@ public final class Settings { * @hide */ public static final String SHOW_MUTE_IN_CRASH_DIALOG = "show_mute_in_crash_dialog"; + + /** + * If nonzero, will show the zen upgrade notification when the user toggles DND on/off. + * @hide + */ + public static final String SHOW_ZEN_UPGRADE_NOTIFICATION = "show_zen_upgrade_notification"; } /** diff --git a/core/java/android/provider/SettingsSlicesContract.java b/core/java/android/provider/SettingsSlicesContract.java index f79d852ddefc..7dc948899dfe 100644 --- a/core/java/android/provider/SettingsSlicesContract.java +++ b/core/java/android/provider/SettingsSlicesContract.java @@ -31,12 +31,12 @@ import android.net.Uri; * <p> * {@link Uri} builder example: * <pre> - * Uri wifiActionUri = AUTHORITY_URI + * Uri wifiActionUri = BASE_URI * .buildUpon() * .appendPath(PATH_SETTING_ACTION) * .appendPath(KEY_WIFI) * .build(); - * Uri bluetoothIntentUri = AUTHORITY_URI + * Uri bluetoothIntentUri = BASE_URI * .buildUpon() * .appendPath(PATH_SETTING_INTENT) * .appendPath(KEY_BLUETOOTH) diff --git a/core/java/android/security/IConfirmationPromptCallback.aidl b/core/java/android/security/IConfirmationPromptCallback.aidl deleted file mode 100644 index 96a1a04828b5..000000000000 --- a/core/java/android/security/IConfirmationPromptCallback.aidl +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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.security; - -/** - * This must be kept manually in sync with system/security/keystore until AIDL - * can generate both Java and C++ bindings. - * - * @hide - */ -interface IConfirmationPromptCallback { - oneway void onConfirmationPromptCompleted(in int result, in byte[] dataThatWasConfirmed); -} diff --git a/core/java/android/security/IKeystoreService.aidl b/core/java/android/security/IKeystoreService.aidl deleted file mode 100644 index 738eb6865230..000000000000 --- a/core/java/android/security/IKeystoreService.aidl +++ /dev/null @@ -1,87 +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.security; - -import android.security.keymaster.ExportResult; -import android.security.keymaster.KeyCharacteristics; -import android.security.keymaster.KeymasterArguments; -import android.security.keymaster.KeymasterCertificateChain; -import android.security.keymaster.KeymasterBlob; -import android.security.keymaster.OperationResult; -import android.security.KeystoreArguments; - -/** - * This must be kept manually in sync with system/security/keystore until AIDL - * can generate both Java and C++ bindings. - * - * @hide - */ -interface IKeystoreService { - int getState(int userId); - byte[] get(String name, int uid); - int insert(String name, in byte[] item, int uid, int flags); - int del(String name, int uid); - int exist(String name, int uid); - String[] list(String namePrefix, int uid); - int reset(); - int onUserPasswordChanged(int userId, String newPassword); - int lock(int userId); - int unlock(int userId, String userPassword); - int isEmpty(int userId); - int generate(String name, int uid, int keyType, int keySize, int flags, - in KeystoreArguments args); - int import_key(String name, in byte[] data, int uid, int flags); - byte[] sign(String name, in byte[] data); - int verify(String name, in byte[] data, in byte[] signature); - byte[] get_pubkey(String name); - String grant(String name, int granteeUid); - int ungrant(String name, int granteeUid); - long getmtime(String name, int uid); - int is_hardware_backed(String string); - int clear_uid(long uid); - - // Keymaster 0.4 methods - int addRngEntropy(in byte[] data, int flags); - int generateKey(String alias, in KeymasterArguments arguments, in byte[] entropy, int uid, - int flags, out KeyCharacteristics characteristics); - int getKeyCharacteristics(String alias, in KeymasterBlob clientId, in KeymasterBlob appId, - int uid, out KeyCharacteristics characteristics); - int importKey(String alias, in KeymasterArguments arguments, int format, - in byte[] keyData, int uid, int flags, out KeyCharacteristics characteristics); - ExportResult exportKey(String alias, int format, in KeymasterBlob clientId, - in KeymasterBlob appId, int uid); - OperationResult begin(IBinder appToken, String alias, int purpose, boolean pruneable, - in KeymasterArguments params, in byte[] entropy, int uid); - OperationResult update(IBinder token, in KeymasterArguments params, in byte[] input); - OperationResult finish(IBinder token, in KeymasterArguments params, in byte[] signature, - in byte[] entropy); - int abort(IBinder handle); - boolean isOperationAuthorized(IBinder token); - int addAuthToken(in byte[] authToken); - int onUserAdded(int userId, int parentId); - int onUserRemoved(int userId); - int attestKey(String alias, in KeymasterArguments params, out KeymasterCertificateChain chain); - int attestDeviceIds(in KeymasterArguments params, out KeymasterCertificateChain chain); - int onDeviceOffBody(); - int importWrappedKey(in String wrappedKeyAlias, in byte[] wrappedKey, - in String wrappingKeyAlias, in byte[] maskingKey, in KeymasterArguments arguments, - in long rootSid, in long fingerprintSid, - out KeyCharacteristics characteristics); - int presentConfirmationPrompt(IBinder listener, String promptText, in byte[] extraData, - in String locale, in int uiOptionsAsFlags); - int cancelConfirmationPrompt(IBinder listener); -} diff --git a/core/java/android/security/KeystoreArguments.aidl b/core/java/android/security/KeystoreArguments.aidl deleted file mode 100644 index dc8ed50182ed..000000000000 --- a/core/java/android/security/KeystoreArguments.aidl +++ /dev/null @@ -1,20 +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.security; - -/* @hide */ -parcelable KeystoreArguments cpp_header "keystore/KeystoreArguments.h"; diff --git a/core/java/android/security/keymaster/ExportResult.aidl b/core/java/android/security/keymaster/ExportResult.aidl deleted file mode 100644 index 17486531a3f0..000000000000 --- a/core/java/android/security/keymaster/ExportResult.aidl +++ /dev/null @@ -1,20 +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.security.keymaster; - -/* @hide */ -parcelable ExportResult cpp_header "keystore/ExportResult.h"; diff --git a/core/java/android/security/keymaster/KeyCharacteristics.aidl b/core/java/android/security/keymaster/KeyCharacteristics.aidl deleted file mode 100644 index 32e75ad267b2..000000000000 --- a/core/java/android/security/keymaster/KeyCharacteristics.aidl +++ /dev/null @@ -1,20 +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.security.keymaster; - -/* @hide */ -parcelable KeyCharacteristics cpp_header "keystore/KeyCharacteristics.h"; diff --git a/core/java/android/security/keymaster/KeymasterArguments.aidl b/core/java/android/security/keymaster/KeymasterArguments.aidl deleted file mode 100644 index 44d9f0954781..000000000000 --- a/core/java/android/security/keymaster/KeymasterArguments.aidl +++ /dev/null @@ -1,20 +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.security.keymaster; - -/* @hide */ -parcelable KeymasterArguments cpp_header "keystore/KeymasterArguments.h"; diff --git a/core/java/android/security/keymaster/KeymasterBlob.aidl b/core/java/android/security/keymaster/KeymasterBlob.aidl deleted file mode 100644 index 5c5db9ec314b..000000000000 --- a/core/java/android/security/keymaster/KeymasterBlob.aidl +++ /dev/null @@ -1,20 +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.security.keymaster; - -/* @hide */ -parcelable KeymasterBlob cpp_header "keystore/KeymasterBlob.h"; diff --git a/core/java/android/security/keymaster/KeymasterCertificateChain.aidl b/core/java/android/security/keymaster/KeymasterCertificateChain.aidl deleted file mode 100644 index ddb5cae1a254..000000000000 --- a/core/java/android/security/keymaster/KeymasterCertificateChain.aidl +++ /dev/null @@ -1,20 +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.security.keymaster; - -/* @hide */ -parcelable KeymasterCertificateChain cpp_header "keystore/KeymasterCertificateChain.h"; diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java index 1d1333504350..f4dcce1e7e58 100644 --- a/core/java/android/security/keymaster/KeymasterDefs.java +++ b/core/java/android/security/keymaster/KeymasterDefs.java @@ -75,6 +75,7 @@ public final class KeymasterDefs { public static final int KM_TAG_ALLOW_WHILE_ON_BODY = KM_BOOL | 506; public static final int KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED = KM_BOOL | 507; public static final int KM_TAG_TRUSTED_CONFIRMATION_REQUIRED = KM_BOOL | 508; + public static final int KM_TAG_UNLOCKED_DEVICE_REQUIRED = KM_BOOL | 509; public static final int KM_TAG_ALL_APPLICATIONS = KM_BOOL | 600; public static final int KM_TAG_APPLICATION_ID = KM_BYTES | 601; @@ -216,6 +217,7 @@ public final class KeymasterDefs { public static final int KM_ERROR_MISSING_MIN_MAC_LENGTH = -58; public static final int KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH = -59; public static final int KM_ERROR_CANNOT_ATTEST_IDS = -66; + public static final int KM_ERROR_DEVICE_LOCKED = -72; public static final int KM_ERROR_UNIMPLEMENTED = -100; public static final int KM_ERROR_VERSION_MISMATCH = -101; public static final int KM_ERROR_UNKNOWN_ERROR = -1000; @@ -262,6 +264,7 @@ public final class KeymasterDefs { sErrorCodeToString.put(KM_ERROR_INVALID_MAC_LENGTH, "Invalid MAC or authentication tag length"); sErrorCodeToString.put(KM_ERROR_CANNOT_ATTEST_IDS, "Unable to attest device ids"); + sErrorCodeToString.put(KM_ERROR_DEVICE_LOCKED, "Device locked"); sErrorCodeToString.put(KM_ERROR_UNIMPLEMENTED, "Not implemented"); sErrorCodeToString.put(KM_ERROR_UNKNOWN_ERROR, "Unknown error"); } diff --git a/core/java/android/security/keymaster/OperationResult.aidl b/core/java/android/security/keymaster/OperationResult.aidl deleted file mode 100644 index db689d46521a..000000000000 --- a/core/java/android/security/keymaster/OperationResult.aidl +++ /dev/null @@ -1,20 +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.security.keymaster; - -/* @hide */ -parcelable OperationResult cpp_header "keystore/OperationResult.h"; diff --git a/core/java/android/security/keystore/OWNERS b/core/java/android/security/keystore/OWNERS new file mode 100644 index 000000000000..bb487fb52c9f --- /dev/null +++ b/core/java/android/security/keystore/OWNERS @@ -0,0 +1,4 @@ +aseemk@google.com +bozhu@google.com +dementyev@google.com +robertberry@google.com diff --git a/core/java/android/service/autofill/AutofillServiceInfo.java b/core/java/android/service/autofill/AutofillServiceInfo.java index 29c6cea14980..70c4ec07ee4e 100644 --- a/core/java/android/service/autofill/AutofillServiceInfo.java +++ b/core/java/android/service/autofill/AutofillServiceInfo.java @@ -32,19 +32,19 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.AttributeSet; import android.util.Log; +import android.util.Pair; import android.util.Xml; import com.android.internal.R; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; - import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; -import java.util.Map; +import java.io.PrintWriter; /** * {@link ServiceInfo} and meta-data about an {@link AutofillService}. @@ -79,7 +79,7 @@ public final class AutofillServiceInfo { private final String mSettingsActivity; @Nullable - private final Map<String, Long> mCompatibilityPackages; + private final ArrayMap<String, Pair<Long, String>> mCompatibilityPackages; public AutofillServiceInfo(Context context, ComponentName comp, int userHandle) throws PackageManager.NameNotFoundException { @@ -117,7 +117,7 @@ public final class AutofillServiceInfo { } String settingsActivity = null; - Map<String, Long> compatibilityPackages = null; + ArrayMap<String, Pair<Long, String>> compatibilityPackages = null; try { final Resources resources = context.getPackageManager().getResourcesForApplication( @@ -153,9 +153,10 @@ public final class AutofillServiceInfo { mCompatibilityPackages = compatibilityPackages; } - private Map<String, Long> parseCompatibilityPackages(XmlPullParser parser, Resources resources) + private ArrayMap<String, Pair<Long, String>> parseCompatibilityPackages(XmlPullParser parser, + Resources resources) throws IOException, XmlPullParserException { - Map<String, Long> compatibilityPackages = null; + ArrayMap<String, Pair<Long, String>> compatibilityPackages = null; final int outerDepth = parser.getDepth(); int type; @@ -199,11 +200,13 @@ public final class AutofillServiceInfo { } else { maxVersionCode = Long.MAX_VALUE; } + final String urlBarResourceId = cpAttributes.getString( + R.styleable.AutofillService_CompatibilityPackage_urlBarResourceId); if (compatibilityPackages == null) { compatibilityPackages = new ArrayMap<>(); } - compatibilityPackages.put(name, maxVersionCode); + compatibilityPackages.put(name, new Pair<>(maxVersionCode, urlBarResourceId)); } finally { XmlUtils.skipCurrentTag(parser); if (cpAttributes != null) { @@ -225,16 +228,21 @@ public final class AutofillServiceInfo { return mSettingsActivity; } + public ArrayMap<String, Pair<Long, String>> getCompatibilityPackages() { + return mCompatibilityPackages; + } + + /** + * Gets the resource id of the URL bar for a package. Used in compat mode + */ + // TODO: return a list of strings instead @Nullable - public boolean isCompatibilityModeRequested(String packageName, long versionCode) { + public String getUrlBarResourceId(String packageName) { if (mCompatibilityPackages == null) { - return false; + return null; } - final Long maxVersionCode = mCompatibilityPackages.get(packageName); - if (maxVersionCode == null) { - return false; - } - return versionCode <= maxVersionCode; + final Pair<Long, String> pair = mCompatibilityPackages.get(packageName); + return pair == null ? null : pair.second; } @Override @@ -247,4 +255,13 @@ public final class AutofillServiceInfo { && !mCompatibilityPackages.isEmpty()).append("]"); return builder.toString(); } + + /** + * Dumps it! + */ + public void dump(String prefix, PrintWriter pw) { + pw.print(prefix); pw.print("Component: "); pw.println(getServiceInfo().getComponentName()); + pw.print(prefix); pw.print("Settings: "); pw.println(mSettingsActivity); + pw.print(prefix); pw.print("Compat packages: "); pw.println(mCompatibilityPackages); + } } diff --git a/core/java/android/service/autofill/DateTransformation.java b/core/java/android/service/autofill/DateTransformation.java index 4e1425d86380..ec24a09a470f 100644 --- a/core/java/android/service/autofill/DateTransformation.java +++ b/core/java/android/service/autofill/DateTransformation.java @@ -20,6 +20,7 @@ import static android.view.autofill.Helper.sDebug; import android.annotation.NonNull; import android.annotation.TestApi; +import android.icu.text.DateFormat; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; @@ -30,7 +31,6 @@ import android.widget.TextView; import com.android.internal.util.Preconditions; -import java.text.DateFormat; import java.util.Date; /** diff --git a/core/java/android/service/autofill/DateValueSanitizer.java b/core/java/android/service/autofill/DateValueSanitizer.java index 0f7b540f8a24..4f797f46da2a 100644 --- a/core/java/android/service/autofill/DateValueSanitizer.java +++ b/core/java/android/service/autofill/DateValueSanitizer.java @@ -21,6 +21,7 @@ import static android.view.autofill.Helper.sDebug; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; +import android.icu.text.DateFormat; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; @@ -28,7 +29,6 @@ import android.view.autofill.AutofillValue; import com.android.internal.util.Preconditions; -import java.text.DateFormat; import java.util.Date; /** diff --git a/core/java/android/service/autofill/FieldClassification.java b/core/java/android/service/autofill/FieldClassification.java index cd1efd68df6f..5bf56cb9c1b5 100644 --- a/core/java/android/service/autofill/FieldClassification.java +++ b/core/java/android/service/autofill/FieldClassification.java @@ -108,21 +108,21 @@ public final class FieldClassification { */ public static final class Match { - private final String mRemoteId; + private final String mCategoryId; private final float mScore; /** @hide */ - public Match(String remoteId, float score) { - mRemoteId = Preconditions.checkNotNull(remoteId); + public Match(String categoryId, float score) { + mCategoryId = Preconditions.checkNotNull(categoryId); mScore = score; } /** - * Gets the remote id of the {@link UserData} entry. + * Gets the category id of the {@link UserData} entry. */ @NonNull - public String getRemoteId() { - return mRemoteId; + public String getCategoryId() { + return mCategoryId; } /** @@ -149,13 +149,13 @@ public final class FieldClassification { public String toString() { if (!sDebug) return super.toString(); - final StringBuilder string = new StringBuilder("Match: remoteId="); - Helper.appendRedacted(string, mRemoteId); + final StringBuilder string = new StringBuilder("Match: categoryId="); + Helper.appendRedacted(string, mCategoryId); return string.append(", score=").append(mScore).toString(); } private void writeToParcel(@NonNull Parcel parcel) { - parcel.writeString(mRemoteId); + parcel.writeString(mCategoryId); parcel.writeFloat(mScore); } diff --git a/core/java/android/service/autofill/FillContext.java b/core/java/android/service/autofill/FillContext.java index cda2f4a23c9a..535c00bc5319 100644 --- a/core/java/android/service/autofill/FillContext.java +++ b/core/java/android/service/autofill/FillContext.java @@ -177,30 +177,6 @@ public final class FillContext implements Parcelable { return foundNodes; } - /** - * Finds the {@link ViewNode} that has the requested {@code id}, if any. - * - * @hide - */ - @Nullable public ViewNode findViewNodeByAutofillId(@NonNull AutofillId id) { - final LinkedList<ViewNode> nodesToProcess = new LinkedList<>(); - final int numWindowNodes = mStructure.getWindowNodeCount(); - for (int i = 0; i < numWindowNodes; i++) { - nodesToProcess.add(mStructure.getWindowNodeAt(i).getRootViewNode()); - } - while (!nodesToProcess.isEmpty()) { - final ViewNode node = nodesToProcess.removeFirst(); - if (id.equals(node.getAutofillId())) { - return node; - } - for (int i = 0; i < node.getChildCount(); i++) { - nodesToProcess.addLast(node.getChildAt(i)); - } - } - - return null; - } - public static final Parcelable.Creator<FillContext> CREATOR = new Parcelable.Creator<FillContext>() { @Override diff --git a/core/java/android/service/autofill/UserData.java b/core/java/android/service/autofill/UserData.java index 6bab6aa82375..a1dd1f846515 100644 --- a/core/java/android/service/autofill/UserData.java +++ b/core/java/android/service/autofill/UserData.java @@ -15,6 +15,7 @@ */ package android.service.autofill; +import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT; import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE; import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE; import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_VALUE_LENGTH; @@ -31,6 +32,7 @@ import android.os.Parcelable; import android.provider.Settings; import android.service.autofill.FieldClassification.Match; import android.text.TextUtils; +import android.util.ArraySet; import android.util.Log; import android.view.autofill.AutofillManager; import android.view.autofill.Helper; @@ -48,23 +50,24 @@ public final class UserData implements Parcelable { private static final String TAG = "UserData"; - private static final int DEFAULT_MAX_USER_DATA_SIZE = 10; + private static final int DEFAULT_MAX_USER_DATA_SIZE = 50; + private static final int DEFAULT_MAX_CATEGORY_COUNT = 10; private static final int DEFAULT_MAX_FIELD_CLASSIFICATION_IDS_SIZE = 10; - private static final int DEFAULT_MIN_VALUE_LENGTH = 5; + private static final int DEFAULT_MIN_VALUE_LENGTH = 3; private static final int DEFAULT_MAX_VALUE_LENGTH = 100; private final String mId; private final String mAlgorithm; private final Bundle mAlgorithmArgs; - private final String[] mRemoteIds; + private final String[] mCategoryIds; private final String[] mValues; private UserData(Builder builder) { mId = builder.mId; mAlgorithm = builder.mAlgorithm; mAlgorithmArgs = builder.mAlgorithmArgs; - mRemoteIds = new String[builder.mRemoteIds.size()]; - builder.mRemoteIds.toArray(mRemoteIds); + mCategoryIds = new String[builder.mCategoryIds.size()]; + builder.mCategoryIds.toArray(mCategoryIds); mValues = new String[builder.mValues.size()]; builder.mValues.toArray(mValues); } @@ -91,8 +94,8 @@ public final class UserData implements Parcelable { } /** @hide */ - public String[] getRemoteIds() { - return mRemoteIds; + public String[] getCategoryIds() { + return mCategoryIds; } /** @hide */ @@ -106,11 +109,11 @@ public final class UserData implements Parcelable { pw.print(prefix); pw.print("Algorithm: "); pw.print(mAlgorithm); pw.print(" Args: "); pw.println(mAlgorithmArgs); - // Cannot disclose remote ids or values because they could contain PII - pw.print(prefix); pw.print("Remote ids size: "); pw.println(mRemoteIds.length); - for (int i = 0; i < mRemoteIds.length; i++) { + // Cannot disclose field ids or values because they could contain PII + pw.print(prefix); pw.print("Field ids size: "); pw.println(mCategoryIds.length); + for (int i = 0; i < mCategoryIds.length; i++) { pw.print(prefix); pw.print(prefix); pw.print(i); pw.print(": "); - pw.println(Helper.getRedacted(mRemoteIds[i])); + pw.println(Helper.getRedacted(mCategoryIds[i])); } pw.print(prefix); pw.print("Values size: "); pw.println(mValues.length); for (int i = 0; i < mValues.length; i++) { @@ -124,6 +127,7 @@ public final class UserData implements Parcelable { pw.print(prefix); pw.print("maxUserDataSize: "); pw.println(getMaxUserDataSize()); pw.print(prefix); pw.print("maxFieldClassificationIdsSize: "); pw.println(getMaxFieldClassificationIdsSize()); + pw.print(prefix); pw.print("maxCategoryCount: "); pw.println(getMaxCategoryCount()); pw.print(prefix); pw.print("minValueLength: "); pw.println(getMinValueLength()); pw.print(prefix); pw.print("maxValueLength: "); pw.println(getMaxValueLength()); } @@ -133,44 +137,59 @@ public final class UserData implements Parcelable { */ public static final class Builder { private final String mId; - private final ArrayList<String> mRemoteIds; + private final ArrayList<String> mCategoryIds; private final ArrayList<String> mValues; private String mAlgorithm; private Bundle mAlgorithmArgs; private boolean mDestroyed; + // Non-persistent array used to limit the number of unique ids. + private final ArraySet<String> mUniqueCategoryIds; + /** * Creates a new builder for the user data used for <a href="#FieldClassification">field * classification</a>. * - * <p>The user data must contain at least one pair of {@code remoteId} -> {@code value}, and - * more pairs can be added through the {@link #add(String, String)} method. + * <p>The user data must contain at least one pair of {@code value} -> {@code categoryId}, + * and more pairs can be added through the {@link #add(String, String)} method. For example: + * + * <pre class="prettyprint"> + * new UserData.Builder("v1", "Bart Simpson", "name") + * .add("bart.simpson@example.com", "email") + * .add("el_barto@example.com", "email") + * .build(); + * </pre> * * @param id id used to identify the whole {@link UserData} object. This id is also returned * by {@link AutofillManager#getUserDataId()}, which can be used to check if the * {@link UserData} is up-to-date without fetching the whole object (through * {@link AutofillManager#getUserData()}). - * @param remoteId unique string used to identify a user data value. + * * @param value value of the user data. + * @param categoryId string used to identify the category the value is associated with. * * @throws IllegalArgumentException if any of the following occurs: * <ol> - * <li>{@code id} is empty - * <li>{@code remoteId} is empty - * <li>{@code value} is empty - * <li>the length of {@code value} is lower than {@link UserData#getMinValueLength()} - * <li>the length of {@code value} is higher than {@link UserData#getMaxValueLength()} + * <li>{@code id} is empty</li> + * <li>{@code categoryId} is empty</li> + * <li>{@code value} is empty</li> + * <li>the length of {@code value} is lower than {@link UserData#getMinValueLength()}</li> + * <li>the length of {@code value} is higher than + * {@link UserData#getMaxValueLength()}</li> * </ol> + * */ - public Builder(@NonNull String id, @NonNull String remoteId, @NonNull String value) { + // TODO(b/70407264): ignore entry instead of throwing exception when settings changed + public Builder(@NonNull String id, @NonNull String value, @NonNull String categoryId) { mId = checkNotEmpty("id", id); - checkNotEmpty("remoteId", remoteId); + checkNotEmpty("categoryId", categoryId); checkValidValue(value); - final int capacity = getMaxUserDataSize(); - mRemoteIds = new ArrayList<>(capacity); - mValues = new ArrayList<>(capacity); - mRemoteIds.add(remoteId); - mValues.add(value); + final int maxUserDataSize = getMaxUserDataSize(); + mCategoryIds = new ArrayList<>(maxUserDataSize); + mValues = new ArrayList<>(maxUserDataSize); + mUniqueCategoryIds = new ArraySet<>(getMaxCategoryCount()); + + addMapping(value, categoryId); } /** @@ -190,6 +209,7 @@ public final class UserData implements Parcelable { */ public Builder setFieldClassificationAlgorithm(@Nullable String name, @Nullable Bundle args) { + throwIfDestroyed(); mAlgorithm = name; mAlgorithmArgs = args; return this; @@ -198,37 +218,58 @@ public final class UserData implements Parcelable { /** * Adds a new value for user data. * - * @param remoteId unique string used to identify the user data. * @param value value of the user data. + * @param categoryId string used to identify the category the value is associated with. * - * @throws IllegalStateException if {@link #build()} or - * {@link #add(String, String)} with the same {@code remoteId} has already - * been called, or if the number of values add (i.e., calls made to this method plus - * constructor) is more than {@link UserData#getMaxUserDataSize()}. + * @throws IllegalStateException if: + * <ol> + * <li>{@link #build()} already called</li> + * <li>the {@code value} has already been added</li> + * <li>the number of unique {@code categoryId} values added so far is more than + * {@link UserData#getMaxCategoryCount()}</li> + * <li>the number of {@code values} added so far is is more than + * {@link UserData#getMaxUserDataSize()}</li> + * </ol> * - * @throws IllegalArgumentException if {@code remoteId} or {@code value} are empty or if the - * length of {@code value} is lower than {@link UserData#getMinValueLength()} - * or higher than {@link UserData#getMaxValueLength()}. + * @throws IllegalArgumentException if any of the following occurs: + * <ol> + * <li>{@code id} is empty</li> + * <li>{@code categoryId} is empty</li> + * <li>{@code value} is empty</li> + * <li>the length of {@code value} is lower than {@link UserData#getMinValueLength()}</li> + * <li>the length of {@code value} is higher than + * {@link UserData#getMaxValueLength()}</li> + * </ol> */ - public Builder add(@NonNull String remoteId, @NonNull String value) { + // TODO(b/70407264): ignore entry instead of throwing exception when settings changed + public Builder add(@NonNull String value, @NonNull String categoryId) { throwIfDestroyed(); - checkNotEmpty("remoteId", remoteId); + checkNotEmpty("categoryId", categoryId); checkValidValue(value); - Preconditions.checkState(!mRemoteIds.contains(remoteId), - // Don't include remoteId on message because it could contain PII - "already has entry with same remoteId"); + if (!mUniqueCategoryIds.contains(categoryId)) { + // New category - check size + Preconditions.checkState(mUniqueCategoryIds.size() < getMaxCategoryCount(), + "already added " + mUniqueCategoryIds.size() + " unique category ids"); + + } + Preconditions.checkState(!mValues.contains(value), - // Don't include remoteId on message because it could contain PII + // Don't include value on message because it could contain PII "already has entry with same value"); - Preconditions.checkState(mRemoteIds.size() < getMaxUserDataSize(), - "already added " + mRemoteIds.size() + " elements"); - mRemoteIds.add(remoteId); - mValues.add(value); + Preconditions.checkState(mValues.size() < getMaxUserDataSize(), + "already added " + mValues.size() + " elements"); + addMapping(value, categoryId); return this; } + private void addMapping(@NonNull String value, @NonNull String categoryId) { + mCategoryIds.add(categoryId); + mValues.add(value); + mUniqueCategoryIds.add(categoryId); + } + private String checkNotEmpty(@NonNull String name, @Nullable String value) { Preconditions.checkNotNull(value); Preconditions.checkArgument(!TextUtils.isEmpty(value), "%s cannot be empty", name); @@ -273,9 +314,9 @@ public final class UserData implements Parcelable { final StringBuilder builder = new StringBuilder("UserData: [id=").append(mId) .append(", algorithm=").append(mAlgorithm); - // Cannot disclose remote ids or values because they could contain PII - builder.append(", remoteIds="); - Helper.appendRedacted(builder, mRemoteIds); + // Cannot disclose category ids or values because they could contain PII + builder.append(", categoryIds="); + Helper.appendRedacted(builder, mCategoryIds); builder.append(", values="); Helper.appendRedacted(builder, mValues); return builder.append("]").toString(); @@ -293,7 +334,7 @@ public final class UserData implements Parcelable { @Override public void writeToParcel(Parcel parcel, int flags) { parcel.writeString(mId); - parcel.writeStringArray(mRemoteIds); + parcel.writeStringArray(mCategoryIds); parcel.writeStringArray(mValues); parcel.writeString(mAlgorithm); parcel.writeBundle(mAlgorithmArgs); @@ -307,12 +348,12 @@ public final class UserData implements Parcelable { // the system obeys the contract of the builder to avoid attacks // using specially crafted parcels. final String id = parcel.readString(); - final String[] remoteIds = parcel.readStringArray(); + final String[] categoryIds = parcel.readStringArray(); final String[] values = parcel.readStringArray(); - final Builder builder = new Builder(id, remoteIds[0], values[0]) + final Builder builder = new Builder(id, values[0], categoryIds[0]) .setFieldClassificationAlgorithm(parcel.readString(), parcel.readBundle()); - for (int i = 1; i < remoteIds.length; i++) { - builder.add(remoteIds[i], values[i]); + for (int i = 1; i < categoryIds.length; i++) { + builder.add(values[i], categoryIds[i]); } return builder.build(); } @@ -340,6 +381,14 @@ public final class UserData implements Parcelable { } /** + * Gets the maximum number of unique category ids that can be passed to + * the builder's constructor and {@link Builder#add(String, String)}. + */ + public static int getMaxCategoryCount() { + return getInt(AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT, DEFAULT_MAX_CATEGORY_COUNT); + } + + /** * Gets the minimum length of values passed to the builder's constructor or * or {@link Builder#add(String, String)}. */ diff --git a/core/java/android/service/euicc/EuiccProfileInfo.java b/core/java/android/service/euicc/EuiccProfileInfo.java index 8e752d1c6c1d..cb4f10455ec9 100644 --- a/core/java/android/service/euicc/EuiccProfileInfo.java +++ b/core/java/android/service/euicc/EuiccProfileInfo.java @@ -17,6 +17,7 @@ package android.service.euicc; import android.annotation.IntDef; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.service.carrier.CarrierIdentifier; @@ -26,15 +27,15 @@ import android.text.TextUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; +import java.util.List; import java.util.Objects; /** * Information about an embedded profile (subscription) on an eUICC. * * @hide - * - * TODO(b/35851809): Make this a SystemApi. */ +@SystemApi public final class EuiccProfileInfo implements Parcelable { /** Profile policy rules (bit mask) */ @@ -44,6 +45,7 @@ public final class EuiccProfileInfo implements Parcelable { POLICY_RULE_DO_NOT_DELETE, POLICY_RULE_DELETE_AFTER_DISABLING }) + /** @hide */ public @interface PolicyRule {} /** Once this profile is enabled, it cannot be disabled. */ public static final int POLICY_RULE_DO_NOT_DISABLE = 1; @@ -60,6 +62,7 @@ public final class EuiccProfileInfo implements Parcelable { PROFILE_CLASS_OPERATIONAL, PROFILE_CLASS_UNSET }) + /** @hide */ public @interface ProfileClass {} /** Testing profiles */ public static final int PROFILE_CLASS_TESTING = 0; @@ -80,6 +83,7 @@ public final class EuiccProfileInfo implements Parcelable { PROFILE_STATE_ENABLED, PROFILE_STATE_UNSET }) + /** @hide */ public @interface ProfileState {} /** Disabled profiles */ public static final int PROFILE_STATE_DISABLED = 0; @@ -92,34 +96,34 @@ public final class EuiccProfileInfo implements Parcelable { public static final int PROFILE_STATE_UNSET = -1; /** The iccid of the subscription. */ - public final String iccid; + private final String mIccid; /** An optional nickname for the subscription. */ - public final @Nullable String nickname; + private final @Nullable String mNickname; /** The service provider name for the subscription. */ - public final String serviceProviderName; + private final String mServiceProviderName; /** The profile name for the subscription. */ - public final String profileName; + private final String mProfileName; /** Profile class for the subscription. */ - @ProfileClass public final int profileClass; + @ProfileClass private final int mProfileClass; /** The profile state of the subscription. */ - @ProfileState public final int state; + @ProfileState private final int mState; /** The operator Id of the subscription. */ - public final CarrierIdentifier carrierIdentifier; + private final CarrierIdentifier mCarrierIdentifier; /** The policy rules of the subscription. */ - @PolicyRule public final int policyRules; + @PolicyRule private final int mPolicyRules; /** * Optional access rules defining which apps can manage this subscription. If unset, only the * platform can manage it. */ - public final @Nullable UiccAccessRule[] accessRules; + private final @Nullable UiccAccessRule[] mAccessRules; public static final Creator<EuiccProfileInfo> CREATOR = new Creator<EuiccProfileInfo>() { @Override @@ -144,51 +148,51 @@ public final class EuiccProfileInfo implements Parcelable { if (!TextUtils.isDigitsOnly(iccid)) { throw new IllegalArgumentException("iccid contains invalid characters: " + iccid); } - this.iccid = iccid; - this.accessRules = accessRules; - this.nickname = nickname; - - this.serviceProviderName = null; - this.profileName = null; - this.profileClass = PROFILE_CLASS_UNSET; - this.state = PROFILE_CLASS_UNSET; - this.carrierIdentifier = null; - this.policyRules = 0; + this.mIccid = iccid; + this.mAccessRules = accessRules; + this.mNickname = nickname; + + this.mServiceProviderName = null; + this.mProfileName = null; + this.mProfileClass = PROFILE_CLASS_UNSET; + this.mState = PROFILE_STATE_UNSET; + this.mCarrierIdentifier = null; + this.mPolicyRules = 0; } private EuiccProfileInfo(Parcel in) { - iccid = in.readString(); - nickname = in.readString(); - serviceProviderName = in.readString(); - profileName = in.readString(); - profileClass = in.readInt(); - state = in.readInt(); + mIccid = in.readString(); + mNickname = in.readString(); + mServiceProviderName = in.readString(); + mProfileName = in.readString(); + mProfileClass = in.readInt(); + mState = in.readInt(); byte exist = in.readByte(); if (exist == (byte) 1) { - carrierIdentifier = CarrierIdentifier.CREATOR.createFromParcel(in); + mCarrierIdentifier = CarrierIdentifier.CREATOR.createFromParcel(in); } else { - carrierIdentifier = null; + mCarrierIdentifier = null; } - policyRules = in.readInt(); - accessRules = in.createTypedArray(UiccAccessRule.CREATOR); + mPolicyRules = in.readInt(); + mAccessRules = in.createTypedArray(UiccAccessRule.CREATOR); } @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeString(iccid); - dest.writeString(nickname); - dest.writeString(serviceProviderName); - dest.writeString(profileName); - dest.writeInt(profileClass); - dest.writeInt(state); - if (carrierIdentifier != null) { + dest.writeString(mIccid); + dest.writeString(mNickname); + dest.writeString(mServiceProviderName); + dest.writeString(mProfileName); + dest.writeInt(mProfileClass); + dest.writeInt(mState); + if (mCarrierIdentifier != null) { dest.writeByte((byte) 1); - carrierIdentifier.writeToParcel(dest, flags); + mCarrierIdentifier.writeToParcel(dest, flags); } else { dest.writeByte((byte) 0); } - dest.writeInt(policyRules); - dest.writeTypedArray(accessRules, flags); + dest.writeInt(mPolicyRules); + dest.writeTypedArray(mAccessRules, flags); } @Override @@ -198,45 +202,50 @@ public final class EuiccProfileInfo implements Parcelable { /** The builder to build a new {@link EuiccProfileInfo} instance. */ public static final class Builder { - public String iccid; - public UiccAccessRule[] accessRules; - public String nickname; - public String serviceProviderName; - public String profileName; - @ProfileClass public int profileClass; - @ProfileState public int state; - public CarrierIdentifier carrierIdentifier; - @PolicyRule public int policyRules; - - public Builder() {} + private String mIccid; + private List<UiccAccessRule> mAccessRules; + private String mNickname; + private String mServiceProviderName; + private String mProfileName; + @ProfileClass private int mProfileClass; + @ProfileState private int mState; + private CarrierIdentifier mCarrierIdentifier; + @PolicyRule private int mPolicyRules; + + public Builder(String value) { + if (!TextUtils.isDigitsOnly(value)) { + throw new IllegalArgumentException("iccid contains invalid characters: " + value); + } + mIccid = value; + } public Builder(EuiccProfileInfo baseProfile) { - iccid = baseProfile.iccid; - nickname = baseProfile.nickname; - serviceProviderName = baseProfile.serviceProviderName; - profileName = baseProfile.profileName; - profileClass = baseProfile.profileClass; - state = baseProfile.state; - carrierIdentifier = baseProfile.carrierIdentifier; - policyRules = baseProfile.policyRules; - accessRules = baseProfile.accessRules; + mIccid = baseProfile.mIccid; + mNickname = baseProfile.mNickname; + mServiceProviderName = baseProfile.mServiceProviderName; + mProfileName = baseProfile.mProfileName; + mProfileClass = baseProfile.mProfileClass; + mState = baseProfile.mState; + mCarrierIdentifier = baseProfile.mCarrierIdentifier; + mPolicyRules = baseProfile.mPolicyRules; + mAccessRules = Arrays.asList(baseProfile.mAccessRules); } /** Builds the profile instance. */ public EuiccProfileInfo build() { - if (iccid == null) { + if (mIccid == null) { throw new IllegalStateException("ICCID must be set for a profile."); } return new EuiccProfileInfo( - iccid, - nickname, - serviceProviderName, - profileName, - profileClass, - state, - carrierIdentifier, - policyRules, - accessRules); + mIccid, + mNickname, + mServiceProviderName, + mProfileName, + mProfileClass, + mState, + mCarrierIdentifier, + mPolicyRules, + mAccessRules); } /** Sets the iccId of the subscription. */ @@ -244,55 +253,55 @@ public final class EuiccProfileInfo implements Parcelable { if (!TextUtils.isDigitsOnly(value)) { throw new IllegalArgumentException("iccid contains invalid characters: " + value); } - iccid = value; + mIccid = value; return this; } /** Sets the nickname of the subscription. */ public Builder setNickname(String value) { - nickname = value; + mNickname = value; return this; } /** Sets the service provider name of the subscription. */ public Builder setServiceProviderName(String value) { - serviceProviderName = value; + mServiceProviderName = value; return this; } /** Sets the profile name of the subscription. */ public Builder setProfileName(String value) { - profileName = value; + mProfileName = value; return this; } /** Sets the profile class of the subscription. */ public Builder setProfileClass(@ProfileClass int value) { - profileClass = value; + mProfileClass = value; return this; } /** Sets the state of the subscription. */ public Builder setState(@ProfileState int value) { - state = value; + mState = value; return this; } /** Sets the carrier identifier of the subscription. */ public Builder setCarrierIdentifier(CarrierIdentifier value) { - carrierIdentifier = value; + mCarrierIdentifier = value; return this; } /** Sets the policy rules of the subscription. */ public Builder setPolicyRules(@PolicyRule int value) { - policyRules = value; + mPolicyRules = value; return this; } /** Sets the access rules of the subscription. */ - public Builder setUiccAccessRule(@Nullable UiccAccessRule[] value) { - accessRules = value; + public Builder setUiccAccessRule(@Nullable List<UiccAccessRule> value) { + mAccessRules = value; return this; } } @@ -306,75 +315,81 @@ public final class EuiccProfileInfo implements Parcelable { @ProfileState int state, CarrierIdentifier carrierIdentifier, @PolicyRule int policyRules, - @Nullable UiccAccessRule[] accessRules) { - this.iccid = iccid; - this.nickname = nickname; - this.serviceProviderName = serviceProviderName; - this.profileName = profileName; - this.profileClass = profileClass; - this.state = state; - this.carrierIdentifier = carrierIdentifier; - this.policyRules = policyRules; - this.accessRules = accessRules; + @Nullable List<UiccAccessRule> accessRules) { + this.mIccid = iccid; + this.mNickname = nickname; + this.mServiceProviderName = serviceProviderName; + this.mProfileName = profileName; + this.mProfileClass = profileClass; + this.mState = state; + this.mCarrierIdentifier = carrierIdentifier; + this.mPolicyRules = policyRules; + if (accessRules != null && accessRules.size() > 0) { + this.mAccessRules = accessRules.toArray(new UiccAccessRule[accessRules.size()]); + } else { + this.mAccessRules = null; + } } /** Gets the ICCID string. */ public String getIccid() { - return iccid; + return mIccid; } /** Gets the access rules. */ @Nullable - public UiccAccessRule[] getUiccAccessRules() { - return accessRules; + public List<UiccAccessRule> getUiccAccessRules() { + if (mAccessRules == null) return null; + return Arrays.asList(mAccessRules); } /** Gets the nickname. */ + @Nullable public String getNickname() { - return nickname; + return mNickname; } /** Gets the service provider name. */ public String getServiceProviderName() { - return serviceProviderName; + return mServiceProviderName; } /** Gets the profile name. */ public String getProfileName() { - return profileName; + return mProfileName; } /** Gets the profile class. */ @ProfileClass public int getProfileClass() { - return profileClass; + return mProfileClass; } /** Gets the state of the subscription. */ @ProfileState public int getState() { - return state; + return mState; } /** Gets the carrier identifier. */ public CarrierIdentifier getCarrierIdentifier() { - return carrierIdentifier; + return mCarrierIdentifier; } /** Gets the policy rules. */ @PolicyRule public int getPolicyRules() { - return policyRules; + return mPolicyRules; } /** Returns whether any policy rule exists. */ public boolean hasPolicyRules() { - return policyRules != 0; + return mPolicyRules != 0; } /** Checks whether a certain policy rule exists. */ public boolean hasPolicyRule(@PolicyRule int policy) { - return (policyRules & policy) != 0; + return (mPolicyRules & policy) != 0; } @Override @@ -387,50 +402,50 @@ public final class EuiccProfileInfo implements Parcelable { } EuiccProfileInfo that = (EuiccProfileInfo) obj; - return Objects.equals(iccid, that.iccid) - && Objects.equals(nickname, that.nickname) - && Objects.equals(serviceProviderName, that.serviceProviderName) - && Objects.equals(profileName, that.profileName) - && profileClass == that.profileClass - && state == that.state - && Objects.equals(carrierIdentifier, that.carrierIdentifier) - && policyRules == that.policyRules - && Arrays.equals(accessRules, that.accessRules); + return Objects.equals(mIccid, that.mIccid) + && Objects.equals(mNickname, that.mNickname) + && Objects.equals(mServiceProviderName, that.mServiceProviderName) + && Objects.equals(mProfileName, that.mProfileName) + && mProfileClass == that.mProfileClass + && mState == that.mState + && Objects.equals(mCarrierIdentifier, that.mCarrierIdentifier) + && mPolicyRules == that.mPolicyRules + && Arrays.equals(mAccessRules, that.mAccessRules); } @Override public int hashCode() { int result = 1; - result = 31 * result + Objects.hashCode(iccid); - result = 31 * result + Objects.hashCode(nickname); - result = 31 * result + Objects.hashCode(serviceProviderName); - result = 31 * result + Objects.hashCode(profileName); - result = 31 * result + profileClass; - result = 31 * result + state; - result = 31 * result + Objects.hashCode(carrierIdentifier); - result = 31 * result + policyRules; - result = 31 * result + Arrays.hashCode(accessRules); + result = 31 * result + Objects.hashCode(mIccid); + result = 31 * result + Objects.hashCode(mNickname); + result = 31 * result + Objects.hashCode(mServiceProviderName); + result = 31 * result + Objects.hashCode(mProfileName); + result = 31 * result + mProfileClass; + result = 31 * result + mState; + result = 31 * result + Objects.hashCode(mCarrierIdentifier); + result = 31 * result + mPolicyRules; + result = 31 * result + Arrays.hashCode(mAccessRules); return result; } @Override public String toString() { return "EuiccProfileInfo (nickname=" - + nickname + + mNickname + ", serviceProviderName=" - + serviceProviderName + + mServiceProviderName + ", profileName=" - + profileName + + mProfileName + ", profileClass=" - + profileClass + + mProfileClass + ", state=" - + state + + mState + ", CarrierIdentifier=" - + carrierIdentifier.toString() + + mCarrierIdentifier.toString() + ", policyRules=" - + policyRules + + mPolicyRules + ", accessRules=" - + Arrays.toString(accessRules) + + Arrays.toString(mAccessRules) + ")"; } } diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java index be8580074f73..b87faef5bb44 100644 --- a/core/java/android/service/euicc/EuiccService.java +++ b/core/java/android/service/euicc/EuiccService.java @@ -17,6 +17,7 @@ package android.service.euicc; import android.annotation.CallSuper; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.app.Service; import android.content.Intent; import android.os.IBinder; @@ -41,8 +42,11 @@ import java.util.concurrent.atomic.AtomicInteger; * <p>To implement the LPA backend, you must extend this class and declare this service in your * manifest file. The service must require the * {@link android.Manifest.permission#BIND_EUICC_SERVICE} permission and include an intent filter - * with the {@link #EUICC_SERVICE_INTERFACE} action. The priority of the intent filter must be set - * to a non-zero value in case multiple implementations are present on the device. For example: + * with the {@link #EUICC_SERVICE_INTERFACE} action. It's suggested that the priority of the intent + * filter to be set to a non-zero value in case multiple implementations are present on the device. + * See the below example. Note that there will be problem if two LPAs are present and they have the + * same priority. + * Example: * * <pre>{@code * <service android:name=".MyEuiccService" @@ -65,9 +69,9 @@ import java.util.concurrent.atomic.AtomicInteger; * filter with the appropriate action, the {@link #CATEGORY_EUICC_UI} category, and a non-zero * priority. * - * TODO(b/35851809): Make this a SystemApi. * @hide */ +@SystemApi public abstract class EuiccService extends Service { /** Action which must be included in this service's intent filter. */ public static final String EUICC_SERVICE_INTERFACE = "android.service.euicc.EuiccService"; @@ -77,7 +81,10 @@ public abstract class EuiccService extends Service { // LUI actions. These are passthroughs of the corresponding EuiccManager actions. - /** @see android.telephony.euicc.EuiccManager#ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS */ + /** + * @see android.telephony.euicc.EuiccManager#ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS + * The difference is this one is used by system to bring up the LUI. + */ public static final String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS = "android.service.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS"; /** @see android.telephony.euicc.EuiccManager#ACTION_PROVISION_EMBEDDED_SUBSCRIPTION */ @@ -88,7 +95,10 @@ public abstract class EuiccService extends Service { // require user interaction. // TODO(b/33075886): Define extras for any input parameters to these dialogs once they are // more scoped out. - /** Alert the user that this action will result in an active SIM being deactivated. */ + /** + * Alert the user that this action will result in an active SIM being deactivated. + * To implement the LUI triggered by the system, you need to define this in AndroidManifest.xml. + */ public static final String ACTION_RESOLVE_DEACTIVATE_SIM = "android.service.euicc.action.RESOLVE_DEACTIVATE_SIM"; /** @@ -102,7 +112,11 @@ public abstract class EuiccService extends Service { public static final String ACTION_RESOLVE_CONFIRMATION_CODE = "android.service.euicc.action.RESOLVE_CONFIRMATION_CODE"; - /** Intent extra set for resolution requests containing the package name of the calling app. */ + /** + * Intent extra set for resolution requests containing the package name of the calling app. + * This is used by the above actions including ACTION_RESOLVE_DEACTIVATE_SIM, + * ACTION_RESOLVE_NO_PRIVILEGES and ACTION_RESOLVE_CONFIRMATION_CODE. + */ public static final String EXTRA_RESOLUTION_CALLING_PACKAGE = "android.service.euicc.extra.RESOLUTION_CALLING_PACKAGE"; @@ -136,10 +150,18 @@ public abstract class EuiccService extends Service { RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_CONFIRMATION_CODE); } - /** Boolean extra for resolution actions indicating whether the user granted consent. */ - public static final String RESOLUTION_EXTRA_CONSENT = "consent"; - /** String extra for resolution actions indicating the carrier confirmation code. */ - public static final String RESOLUTION_EXTRA_CONFIRMATION_CODE = "confirmation_code"; + /** + * Boolean extra for resolution actions indicating whether the user granted consent. + * This is used and set by the implementation and used in {@code EuiccOperation}. + */ + public static final String EXTRA_RESOLUTION_CONSENT = + "android.service.euicc.extra.RESOLUTION_CONSENT"; + /** + * String extra for resolution actions indicating the carrier confirmation code. + * This is used and set by the implementation and used in {@code EuiccOperation}. + */ + public static final String EXTRA_RESOLUTION_CONFIRMATION_CODE = + "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE"; private final IEuiccService.Stub mStubWrapper; @@ -199,9 +221,9 @@ public abstract class EuiccService extends Service { * * @see IEuiccService#startOtaIfNecessary */ - public interface OtaStatusChangedCallback { + public abstract static class OtaStatusChangedCallback { /** Called when OTA status is changed. */ - void onOtaStatusChanged(int status); + public abstract void onOtaStatusChanged(int status); } /** @@ -238,8 +260,7 @@ public abstract class EuiccService extends Service { /** * Populate {@link DownloadableSubscription} metadata for the given downloadable subscription. * - * @param slotId ID of the SIM slot to use for the operation. This is currently not populated - * but is here to future-proof the APIs. + * @param slotId ID of the SIM slot to use for the operation. * @param subscription A subscription whose metadata needs to be populated. * @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the * eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM)} @@ -267,8 +288,7 @@ public abstract class EuiccService extends Service { /** * Download the given subscription. * - * @param slotId ID of the SIM slot to use for the operation. This is currently not populated - * but is here to future-proof the APIs. + * @param slotId ID of the SIM slot to use for the operation. * @param subscription The subscription to download. * @param switchAfterDownload If true, the subscription should be enabled upon successful * download. @@ -286,8 +306,7 @@ public abstract class EuiccService extends Service { /** * Return a list of all @link EuiccProfileInfo}s. * - * @param slotId ID of the SIM slot to use for the operation. This is currently not populated - * but is here to future-proof the APIs. + * @param slotId ID of the SIM slot to use for the operation. * @return The result of the operation. * @see android.telephony.SubscriptionManager#getAvailableSubscriptionInfoList * @see android.telephony.SubscriptionManager#getAccessibleSubscriptionInfoList @@ -297,8 +316,7 @@ public abstract class EuiccService extends Service { /** * Return info about the eUICC chip/device. * - * @param slotId ID of the SIM slot to use for the operation. This is currently not populated - * but is here to future-proof the APIs. + * @param slotId ID of the SIM slot to use for the operation. * @return the {@link EuiccInfo} for the eUICC chip/device. * @see android.telephony.euicc.EuiccManager#getEuiccInfo */ @@ -310,8 +328,7 @@ public abstract class EuiccService extends Service { * <p>If the subscription is currently active, it should be deactivated first (equivalent to a * physical SIM being ejected). * - * @param slotId ID of the SIM slot to use for the operation. This is currently not populated - * but is here to future-proof the APIs. + * @param slotId ID of the SIM slot to use for the operation. * @param iccid the ICCID of the subscription to delete. * @return the result of the delete operation. May be one of the predefined {@code RESULT_} * constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}. @@ -322,8 +339,7 @@ public abstract class EuiccService extends Service { /** * Switch to the given subscription. * - * @param slotId ID of the SIM slot to use for the operation. This is currently not populated - * but is here to future-proof the APIs. + * @param slotId ID of the SIM slot to use for the operation. * @param iccid the ICCID of the subscription to enable. May be null, in which case the current * profile should be deactivated and no profile should be activated to replace it - this is * equivalent to a physical SIM being ejected. @@ -340,8 +356,7 @@ public abstract class EuiccService extends Service { /** * Update the nickname of the given subscription. * - * @param slotId ID of the SIM slot to use for the operation. This is currently not populated - * but is here to future-proof the APIs. + * @param slotId ID of the SIM slot to use for the operation. * @param iccid the ICCID of the subscription to update. * @param nickname the new nickname to apply. * @return the result of the update operation. May be one of the predefined {@code RESULT_} diff --git a/core/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java b/core/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java index 5a24492007f3..e2171ae6bc76 100644 --- a/core/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java +++ b/core/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java @@ -16,16 +16,19 @@ package android.service.euicc; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.telephony.euicc.DownloadableSubscription; +import java.util.Arrays; +import java.util.List; + /** * Result of a {@link EuiccService#onGetDefaultDownloadableSubscriptionList} operation. * @hide - * - * TODO(b/35851809): Make this a SystemApi. */ +@SystemApi public final class GetDefaultDownloadableSubscriptionListResult implements Parcelable { public static final Creator<GetDefaultDownloadableSubscriptionListResult> CREATOR = @@ -42,20 +45,35 @@ public final class GetDefaultDownloadableSubscriptionListResult implements Parce }; /** - * Result of the operation. + * @hide + * @deprecated - Do no use. Use getResult() instead. + */ + @Deprecated + public final int result; + + @Nullable + private final DownloadableSubscription[] mSubscriptions; + + /** + * Gets the result of the operation. * * <p>May be one of the predefined {@code RESULT_} constants in EuiccService or any * implementation-specific code starting with {@link EuiccService#RESULT_FIRST_USER}. */ - public final int result; + public int getResult() { + return result; + } /** - * The available {@link DownloadableSubscription}s (with filled-in metadata). + * Gets the available {@link DownloadableSubscription}s (with filled-in metadata). * * <p>Only non-null if {@link #result} is {@link EuiccService#RESULT_OK}. */ @Nullable - public final DownloadableSubscription[] subscriptions; + public List<DownloadableSubscription> getDownloadableSubscriptions() { + if (mSubscriptions == null) return null; + return Arrays.asList(mSubscriptions); + } /** * Construct a new {@link GetDefaultDownloadableSubscriptionListResult}. @@ -70,25 +88,25 @@ public final class GetDefaultDownloadableSubscriptionListResult implements Parce @Nullable DownloadableSubscription[] subscriptions) { this.result = result; if (this.result == EuiccService.RESULT_OK) { - this.subscriptions = subscriptions; + this.mSubscriptions = subscriptions; } else { if (subscriptions != null) { throw new IllegalArgumentException( "Error result with non-null subscriptions: " + result); } - this.subscriptions = null; + this.mSubscriptions = null; } } private GetDefaultDownloadableSubscriptionListResult(Parcel in) { this.result = in.readInt(); - this.subscriptions = in.createTypedArray(DownloadableSubscription.CREATOR); + this.mSubscriptions = in.createTypedArray(DownloadableSubscription.CREATOR); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(result); - dest.writeTypedArray(subscriptions, flags); + dest.writeTypedArray(mSubscriptions, flags); } @Override diff --git a/core/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java b/core/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java index de8a30706945..1edb5398add4 100644 --- a/core/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java +++ b/core/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java @@ -16,6 +16,7 @@ package android.service.euicc; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.telephony.euicc.DownloadableSubscription; @@ -23,9 +24,8 @@ import android.telephony.euicc.DownloadableSubscription; /** * Result of a {@link EuiccService#onGetDownloadableSubscriptionMetadata} operation. * @hide - * - * TODO(b/35851809): Make this a SystemApi. */ +@SystemApi public final class GetDownloadableSubscriptionMetadataResult implements Parcelable { public static final Creator<GetDownloadableSubscriptionMetadataResult> CREATOR = @@ -42,20 +42,34 @@ public final class GetDownloadableSubscriptionMetadataResult implements Parcelab }; /** - * Result of the operation. + * @hide + * @deprecated - Do no use. Use getResult() instead. + */ + @Deprecated + public final int result; + + @Nullable + private final DownloadableSubscription mSubscription; + + /** + * Gets the result of the operation. * * <p>May be one of the predefined {@code RESULT_} constants in EuiccService or any * implementation-specific code starting with {@link EuiccService#RESULT_FIRST_USER}. */ - public final int result; + public int getResult() { + return result; + } /** - * The {@link DownloadableSubscription} with filled-in metadata. + * Gets the {@link DownloadableSubscription} with filled-in metadata. * * <p>Only non-null if {@link #result} is {@link EuiccService#RESULT_OK}. */ @Nullable - public final DownloadableSubscription subscription; + public DownloadableSubscription getDownloadableSubscription() { + return mSubscription; + } /** * Construct a new {@link GetDownloadableSubscriptionMetadataResult}. @@ -70,25 +84,25 @@ public final class GetDownloadableSubscriptionMetadataResult implements Parcelab @Nullable DownloadableSubscription subscription) { this.result = result; if (this.result == EuiccService.RESULT_OK) { - this.subscription = subscription; + this.mSubscription = subscription; } else { if (subscription != null) { throw new IllegalArgumentException( "Error result with non-null subscription: " + result); } - this.subscription = null; + this.mSubscription = null; } } private GetDownloadableSubscriptionMetadataResult(Parcel in) { this.result = in.readInt(); - this.subscription = in.readTypedObject(DownloadableSubscription.CREATOR); + this.mSubscription = in.readTypedObject(DownloadableSubscription.CREATOR); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(result); - dest.writeTypedObject(this.subscription, flags); + dest.writeTypedObject(this.mSubscription, flags); } @Override diff --git a/core/java/android/service/euicc/GetEuiccProfileInfoListResult.java b/core/java/android/service/euicc/GetEuiccProfileInfoListResult.java index 7ad84888dc82..464d136e70e5 100644 --- a/core/java/android/service/euicc/GetEuiccProfileInfoListResult.java +++ b/core/java/android/service/euicc/GetEuiccProfileInfoListResult.java @@ -16,15 +16,18 @@ package android.service.euicc; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; +import java.util.Arrays; +import java.util.List; + /** * Result of a {@link EuiccService#onGetEuiccProfileInfoList} operation. * @hide - * - * TODO(b/35851809): Make this a SystemApi. */ +@SystemApi public final class GetEuiccProfileInfoListResult implements Parcelable { public static final Creator<GetEuiccProfileInfoListResult> CREATOR = @@ -41,19 +44,38 @@ public final class GetEuiccProfileInfoListResult implements Parcelable { }; /** - * Result of the operation. + * @hide + * @deprecated - Do no use. Use getResult() instead. + */ + @Deprecated + public final int result; + + @Nullable + private final EuiccProfileInfo[] mProfiles; + + private final boolean mIsRemovable; + + /** + * Gets the result of the operation. * * <p>May be one of the predefined {@code RESULT_} constants in EuiccService or any * implementation-specific code starting with {@link EuiccService#RESULT_FIRST_USER}. */ - public final int result; + public int getResult() { + return result; + } - /** The profile list (only upon success). */ + /** Gets the profile list (only upon success). */ @Nullable - public final EuiccProfileInfo[] profiles; + public List<EuiccProfileInfo> getProfiles() { + if (mProfiles == null) return null; + return Arrays.asList(mProfiles); + } - /** Whether the eUICC is removable. */ - public final boolean isRemovable; + /** Gets whether the eUICC is removable. */ + public boolean getIsRemovable() { + return mIsRemovable; + } /** * Construct a new {@link GetEuiccProfileInfoListResult}. @@ -71,30 +93,29 @@ public final class GetEuiccProfileInfoListResult implements Parcelable { public GetEuiccProfileInfoListResult( int result, @Nullable EuiccProfileInfo[] profiles, boolean isRemovable) { this.result = result; - this.isRemovable = isRemovable; + this.mIsRemovable = isRemovable; if (this.result == EuiccService.RESULT_OK) { - this.profiles = profiles; + this.mProfiles = profiles; } else { if (profiles != null) { throw new IllegalArgumentException( "Error result with non-null profiles: " + result); } - this.profiles = null; + this.mProfiles = null; } - } private GetEuiccProfileInfoListResult(Parcel in) { this.result = in.readInt(); - this.profiles = in.createTypedArray(EuiccProfileInfo.CREATOR); - this.isRemovable = in.readBoolean(); + this.mProfiles = in.createTypedArray(EuiccProfileInfo.CREATOR); + this.mIsRemovable = in.readBoolean(); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(result); - dest.writeTypedArray(profiles, flags); - dest.writeBoolean(isRemovable); + dest.writeTypedArray(mProfiles, flags); + dest.writeBoolean(mIsRemovable); } @Override diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index b7b2b2de35e5..422e36baee70 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -1343,6 +1343,7 @@ public abstract class NotificationListenerService extends Service { /** * @hide */ + @GuardedBy("mLock") public final void applyUpdateLocked(NotificationRankingUpdate update) { mRankingMap = new RankingMap(update); } diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index d66322c3aa89..171d4d938beb 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -94,7 +94,7 @@ public class ZenModeConfig implements Parcelable { private static final boolean DEFAULT_ALLOW_SCREEN_OFF = true; private static final boolean DEFAULT_ALLOW_SCREEN_ON = true; - private static final int XML_VERSION = 2; + public static final int XML_VERSION = 3; public static final String ZEN_TAG = "zen"; private static final String ZEN_ATT_VERSION = "version"; private static final String ZEN_ATT_USER = "user"; @@ -145,6 +145,7 @@ public class ZenModeConfig implements Parcelable { public int user = UserHandle.USER_SYSTEM; public boolean allowWhenScreenOff = DEFAULT_ALLOW_SCREEN_OFF; public boolean allowWhenScreenOn = DEFAULT_ALLOW_SCREEN_ON; + public int version; public ZenRule manualRule; public ArrayMap<String, ZenRule> automaticRules = new ArrayMap<>(); @@ -431,6 +432,7 @@ public class ZenModeConfig implements Parcelable { String tag = parser.getName(); if (!ZEN_TAG.equals(tag)) return null; final ZenModeConfig rt = new ZenModeConfig(); + rt.version = safeInt(parser, ZEN_ATT_VERSION, XML_VERSION); rt.user = safeInt(parser, ZEN_ATT_USER, rt.user); while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { tag = parser.getName(); diff --git a/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java b/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java index 704a1daf3ec7..2ed618b39f1f 100644 --- a/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java +++ b/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java @@ -15,17 +15,17 @@ */ package android.speech.tts; +import android.media.AudioTrack; import android.speech.tts.TextToSpeechService.AudioOutputParams; import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher; -import android.media.AudioTrack; import android.util.Log; import java.util.LinkedList; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; -import java.util.concurrent.ConcurrentLinkedQueue; /** * Manages the playback of a list of byte arrays representing audio data that are queued by the @@ -157,9 +157,16 @@ final class SynthesisPlaybackQueueItem extends PlaybackQueueItem mStopped = true; mStatusCode = statusCode; + // Wake up the synthesis thread if it was waiting on put(). Its + // buffers will no longer be copied since mStopped is true. The + // PlaybackSynthesisCallback that this synthesis corresponds to + // would also have been stopped, and so all calls to + // Callback.onDataAvailable( ) will return errors too. + mNotFull.signal(); + if (mRunState.getAndSet(STOP_CALLED) == NOT_RUN) { - // Dispatch the status code and just finish without signaling - // if run() has not even started. + // Dispatch the status code and just finish. Signaling audio + // playback is not necessary because run() hasn't started. dispatchEndStatus(); return; } @@ -168,13 +175,6 @@ final class SynthesisPlaybackQueueItem extends PlaybackQueueItem // take() will return null since mStopped was true, and will then // break out of the data write loop. mReadReady.signal(); - - // Wake up the synthesis thread if it was waiting on put(). Its - // buffers will no longer be copied since mStopped is true. The - // PlaybackSynthesisCallback that this synthesis corresponds to - // would also have been stopped, and so all calls to - // Callback.onDataAvailable( ) will return errors too. - mNotFull.signal(); } finally { mListLock.unlock(); } diff --git a/core/java/android/text/OWNERS b/core/java/android/text/OWNERS index 0f85e1f9c5d9..e56137119c28 100644 --- a/core/java/android/text/OWNERS +++ b/core/java/android/text/OWNERS @@ -1,4 +1,5 @@ +set noparent + siyamed@google.com nona@google.com clarabayarri@google.com -toki@google.com diff --git a/core/java/android/text/style/TypefaceSpan.java b/core/java/android/text/style/TypefaceSpan.java index 162281250208..908de2988be9 100644 --- a/core/java/android/text/style/TypefaceSpan.java +++ b/core/java/android/text/style/TypefaceSpan.java @@ -17,6 +17,8 @@ package android.text.style; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.graphics.LeakyTypefaceStorage; import android.graphics.Paint; import android.graphics.Typeface; import android.os.Parcel; @@ -25,33 +27,69 @@ import android.text.TextPaint; import android.text.TextUtils; /** - * Changes the typeface family of the text to which the span is attached. Examples of typeface - * family include "monospace", "serif", and "sans-serif". + * Span that updates the typeface of the text it's attached to. The <code>TypefaceSpan</code> can + * be constructed either based on a font family or based on a <code>Typeface</code>. When + * {@link #TypefaceSpan(String)} is used, the previous style of the <code>TextView</code> is kept. + * When {@link #TypefaceSpan(Typeface)} is used, the <code>Typeface</code> style replaces the + * <code>TextView</code>'s style. * <p> - * For example, change the typeface of a text to "monospace" like this: + * For example, let's consider a <code>TextView</code> with + * <code>android:textStyle="italic"</code> and a typeface created based on a font from resources, + * with a bold style. When applying a <code>TypefaceSpan</code> based the typeface, the text will + * only keep the bold style, overriding the <code>TextView</code>'s textStyle. When applying a + * <code>TypefaceSpan</code> based on a font family: "monospace", the resulted text will keep the + * italic style. * <pre> - * SpannableString string = new SpannableString("Text with typeface span"); - * string.setSpan(new TypefaceSpan("monospace"), 10, 18, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + * Typeface myTypeface = Typeface.create(ResourcesCompat.getFont(context, R.font.acme), + * Typeface.BOLD); + * SpannableString string = new SpannableString("Text with typeface span."); + * string.setSpan(new TypefaceSpan(myTypeface), 10, 18, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + * string.setSpan(new TypefaceSpan("monospace"), 19, 22, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); * </pre> * <img src="{@docRoot}reference/android/images/text/style/typefacespan.png" /> - * <figcaption>Text with "monospace" typeface family.</figcaption> + * <figcaption>Text with <code>TypefaceSpan</code>s constructed based on a font from resource and + * from a font family.</figcaption> */ public class TypefaceSpan extends MetricAffectingSpan implements ParcelableSpan { + @Nullable private final String mFamily; + @Nullable + private final Typeface mTypeface; + /** - * Constructs a {@link TypefaceSpan} based on a font family. + * Constructs a {@link TypefaceSpan} based on the font family. The previous style of the + * TextPaint is kept. If the font family is null, the text paint is not modified. * - * @param family The font family for this typeface. Examples include - * "monospace", "serif", and "sans-serif". + * @param family The font family for this typeface. Examples include + * "monospace", "serif", and "sans-serif" */ - public TypefaceSpan(String family) { - mFamily = family; + public TypefaceSpan(@Nullable String family) { + this(family, null); } + /** + * Constructs a {@link TypefaceSpan} from a {@link Typeface}. The previous style of the + * TextPaint is overridden and the style of the typeface is used. + * + * @param typeface the typeface + */ + public TypefaceSpan(@NonNull Typeface typeface) { + this(null, typeface); + } + + /** + * Constructs a {@link TypefaceSpan} from a parcel. + */ public TypefaceSpan(@NonNull Parcel src) { mFamily = src.readString(); + mTypeface = LeakyTypefaceStorage.readTypefaceFromParcel(src); + } + + private TypefaceSpan(@Nullable String family, @Nullable Typeface typeface) { + mFamily = family; + mTypeface = typeface; } @Override @@ -79,37 +117,59 @@ public class TypefaceSpan extends MetricAffectingSpan implements ParcelableSpan @Override public void writeToParcelInternal(@NonNull Parcel dest, int flags) { dest.writeString(mFamily); + LeakyTypefaceStorage.writeTypefaceToParcel(mTypeface, dest); } /** - * Returns the font family name. + * Returns the font family name set in the span. + * + * @return the font family name + * @see #TypefaceSpan(String) */ + @Nullable public String getFamily() { return mFamily; } + /** + * Returns the typeface set in the span. + * + * @return the typeface set + * @see #TypefaceSpan(Typeface) + */ + @Nullable + public Typeface getTypeface() { + return mTypeface; + } + @Override - public void updateDrawState(@NonNull TextPaint textPaint) { - apply(textPaint, mFamily); + public void updateDrawState(@NonNull TextPaint ds) { + updateTypeface(ds); } @Override - public void updateMeasureState(@NonNull TextPaint textPaint) { - apply(textPaint, mFamily); + public void updateMeasureState(@NonNull TextPaint paint) { + updateTypeface(paint); } - private static void apply(@NonNull Paint paint, String family) { - int oldStyle; + private void updateTypeface(@NonNull Paint paint) { + if (mTypeface != null) { + paint.setTypeface(mTypeface); + } else if (mFamily != null) { + applyFontFamily(paint, mFamily); + } + } + private void applyFontFamily(@NonNull Paint paint, @NonNull String family) { + int style; Typeface old = paint.getTypeface(); if (old == null) { - oldStyle = 0; + style = Typeface.NORMAL; } else { - oldStyle = old.getStyle(); + style = old.getStyle(); } - - Typeface tf = Typeface.create(family, oldStyle); - int fake = oldStyle & ~tf.getStyle(); + final Typeface styledTypeface = Typeface.create(family, style); + int fake = style & ~styledTypeface.getStyle(); if ((fake & Typeface.BOLD) != 0) { paint.setFakeBoldText(true); @@ -118,7 +178,6 @@ public class TypefaceSpan extends MetricAffectingSpan implements ParcelableSpan if ((fake & Typeface.ITALIC) != 0) { paint.setTextSkewX(-0.25f); } - - paint.setTypeface(tf); + paint.setTypeface(styledTypeface); } } diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java index d973d4ac076c..3a22db2bfb22 100644 --- a/core/java/android/text/util/Linkify.java +++ b/core/java/android/text/util/Linkify.java @@ -644,7 +644,13 @@ public class Linkify { @Nullable Runnable modifyTextView) { Preconditions.checkNotNull(text); Preconditions.checkNotNull(classifier); - final Supplier<TextLinks> supplier = () -> classifier.generateLinks(text, options); + + // The input text may exceed the maximum length the text classifier can handle. In such + // cases, we process the text up to the maximum length. + final CharSequence truncatedText = text.subSequence( + 0, Math.min(text.length(), classifier.getMaxGenerateLinksTextLength())); + + final Supplier<TextLinks> supplier = () -> classifier.generateLinks(truncatedText, options); final Consumer<TextLinks> consumer = links -> { if (links.getLinks().isEmpty()) { if (callback != null) { @@ -653,7 +659,8 @@ public class Linkify { return; } - final TextLinkSpan[] old = text.getSpans(0, text.length(), TextLinkSpan.class); + // Remove spans only for the part of the text we generated links for. + final TextLinkSpan[] old = text.getSpans(0, truncatedText.length(), TextLinkSpan.class); for (int i = old.length - 1; i >= 0; i--) { text.removeSpan(old[i]); } @@ -662,7 +669,8 @@ public class Linkify { ? null : options.getSpanFactory(); final @TextLinks.ApplyStrategy int applyStrategy = (options == null) ? TextLinks.APPLY_STRATEGY_IGNORE : options.getApplyStrategy(); - final @TextLinks.Status int result = links.apply(text, applyStrategy, spanFactory); + final @TextLinks.Status int result = links.apply(text, applyStrategy, spanFactory, + true /*allowPrefix*/); if (result == TextLinks.STATUS_LINKS_APPLIED) { if (modifyTextView != null) { modifyTextView.run(); diff --git a/core/java/android/transition/ArcMotion.java b/core/java/android/transition/ArcMotion.java index da1483499220..172c8376c8cc 100644 --- a/core/java/android/transition/ArcMotion.java +++ b/core/java/android/transition/ArcMotion.java @@ -216,7 +216,13 @@ public class ArcMotion extends PathMotion { boolean isMovingUpwards = startY > endY; - if ((Math.abs(deltaX) < Math.abs(deltaY))) { + if (deltaY == 0) { + ex = dx; + ey = dy + (Math.abs(deltaX) * 0.5f * mMinimumHorizontalTangent); + } else if (deltaX == 0) { + ex = dx + (Math.abs(deltaY) * 0.5f * mMinimumVerticalTangent); + ey = dy; + } else if ((Math.abs(deltaX) < Math.abs(deltaY))) { // Similar triangles bfa and bde mean that (ab/fb = eb/bd) // Therefore, eb = ab * bd / fb // ab = hypotenuse @@ -254,7 +260,7 @@ public class ArcMotion extends PathMotion { float maximumArcDist2 = midDist2 * mMaximumTangent * mMaximumTangent; float newArcDistance2 = 0; - if (arcDist2 < minimumArcDist2) { + if (arcDist2 != 0 && arcDist2 < minimumArcDist2) { newArcDistance2 = minimumArcDist2; } else if (arcDist2 > maximumArcDist2) { newArcDistance2 = maximumArcDist2; diff --git a/core/java/android/transition/TransitionUtils.java b/core/java/android/transition/TransitionUtils.java index 60b77bc8d426..46c9e0c7202f 100644 --- a/core/java/android/transition/TransitionUtils.java +++ b/core/java/android/transition/TransitionUtils.java @@ -20,14 +20,13 @@ import android.animation.Animator; import android.animation.AnimatorSet; import android.animation.TypeEvaluator; import android.graphics.Bitmap; +import android.graphics.Canvas; import android.graphics.Matrix; +import android.graphics.Picture; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; -import android.view.DisplayListCanvas; -import android.view.RenderNode; -import android.view.ThreadedRenderer; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; @@ -128,10 +127,8 @@ public class TransitionUtils { } int bitmapWidth = (int) (width * scale); int bitmapHeight = (int) (height * scale); - final RenderNode node = RenderNode.create("TransitionUtils", hostView); - node.setLeftTopRightBottom(0, 0, width, height); - node.setClipToBounds(false); - final DisplayListCanvas canvas = node.start(width, height); + final Picture picture = new Picture(); + final Canvas canvas = picture.beginRecording(width, height); // Do stuff with the canvas Rect existingBounds = drawable.getBounds(); int left = existingBounds.left; @@ -141,8 +138,8 @@ public class TransitionUtils { drawable.setBounds(0, 0, bitmapWidth, bitmapHeight); drawable.draw(canvas); drawable.setBounds(left, top, right, bottom); - node.end(canvas); - return ThreadedRenderer.createHardwareBitmap(node, width, height); + picture.endRecording(); + return Bitmap.createBitmap(picture); } /** @@ -183,14 +180,12 @@ public class TransitionUtils { matrix.postTranslate(-bounds.left, -bounds.top); matrix.postScale(scale, scale); - final RenderNode node = RenderNode.create("TransitionUtils", view); - node.setLeftTopRightBottom(0, 0, bitmapWidth, bitmapHeight); - node.setClipToBounds(false); - final DisplayListCanvas canvas = node.start(bitmapWidth, bitmapHeight); + final Picture picture = new Picture(); + final Canvas canvas = picture.beginRecording(bitmapWidth, bitmapHeight); canvas.concat(matrix); view.draw(canvas); - node.end(canvas); - bitmap = ThreadedRenderer.createHardwareBitmap(node, bitmapWidth, bitmapHeight); + picture.endRecording(); + bitmap = Bitmap.createBitmap(picture); } if (addToOverlay) { sceneRoot.getOverlay().remove(view); diff --git a/core/java/android/util/AttributeSet.java b/core/java/android/util/AttributeSet.java index eb8c1682fb7c..7f327c7b65ed 100644 --- a/core/java/android/util/AttributeSet.java +++ b/core/java/android/util/AttributeSet.java @@ -17,6 +17,8 @@ package android.util; +import org.xmlpull.v1.XmlPullParser; + /** * A collection of attributes, as found associated with a tag in an XML * document. Often you will not want to use this interface directly, instead @@ -54,18 +56,42 @@ package android.util; * compiled XML resource that is not available in a normal XML file, such * as {@link #getAttributeNameResource(int)} which returns the resource * identifier associated with a particular XML attribute name. + * + * @see XmlPullParser */ public interface AttributeSet { /** * Returns the number of attributes available in the set. - * + * + * <p>See also {@link XmlPullParser#getAttributeCount XmlPullParser.getAttributeCount()}, + * which this method corresponds to when parsing a compiled XML file.</p> + * * @return A positive integer, or 0 if the set is empty. */ public int getAttributeCount(); /** + * Returns the namespace of the specified attribute. + * + * <p>See also {@link XmlPullParser#getAttributeNamespace XmlPullParser.getAttributeNamespace()}, + * which this method corresponds to when parsing a compiled XML file.</p> + * + * @param index Index of the desired attribute, 0...count-1. + * + * @return A String containing the namespace of the attribute, or null if th + * attribute cannot be found. + */ + default String getAttributeNamespace (int index) { + // This is a new method since the first interface definition, so add stub impl. + return null; + } + + /** * Returns the name of the specified attribute. - * + * + * <p>See also {@link XmlPullParser#getAttributeName XmlPullParser.getAttributeName()}, + * which this method corresponds to when parsing a compiled XML file.</p> + * * @param index Index of the desired attribute, 0...count-1. * * @return A String containing the name of the attribute, or null if the diff --git a/core/java/android/util/ByteStringUtils.java b/core/java/android/util/ByteStringUtils.java index 333208db5f79..f6460ad90f6b 100644 --- a/core/java/android/util/ByteStringUtils.java +++ b/core/java/android/util/ByteStringUtils.java @@ -22,61 +22,63 @@ package android.util; * @hide */ public final class ByteStringUtils { - private final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); + private static final char[] HEX_LOWERCASE_ARRAY = "0123456789abcdef".toCharArray(); + private static final char[] HEX_UPPERCASE_ARRAY = "0123456789ABCDEF".toCharArray(); - private ByteStringUtils() { + private ByteStringUtils() { /* hide constructor */ - } - - /** - * Returns the hex encoded string representation of bytes. - * @param bytes Byte array to encode. - * @return Hex encoded string representation of bytes. - */ - public static String toHexString(byte[] bytes) { - if (bytes == null || bytes.length == 0 || bytes.length % 2 != 0) { - return null; } - final int byteLength = bytes.length; - final int charCount = 2 * byteLength; - final char[] chars = new char[charCount]; + /** + * Returns the hex encoded string representation of bytes. + * @param bytes Byte array to encode. + * @return Hex encoded string representation of bytes. + */ + public static String toHexString(byte[] bytes) { + if (bytes == null || bytes.length == 0 || bytes.length % 2 != 0) { + return null; + } - for (int i = 0; i < byteLength; i++) { - final int byteHex = bytes[i] & 0xFF; - chars[i * 2] = HEX_ARRAY[byteHex >>> 4]; - chars[i * 2 + 1] = HEX_ARRAY[byteHex & 0x0F]; - } - return new String(chars); - } + final int byteLength = bytes.length; + final int charCount = 2 * byteLength; + final char[] chars = new char[charCount]; - /** - * Returns the decoded byte array representation of str. - * @param str Hex encoded string to decode. - * @return Decoded byte array representation of str. - */ - public static byte[] fromHexToByteArray(String str) { - if (str == null || str.length() == 0 || str.length() % 2 != 0) { - return null; + for (int i = 0; i < byteLength; i++) { + final int byteHex = bytes[i] & 0xFF; + chars[i * 2] = HEX_UPPERCASE_ARRAY[byteHex >>> 4]; + chars[i * 2 + 1] = HEX_UPPERCASE_ARRAY[byteHex & 0x0F]; + } + return new String(chars); } - final char[] chars = str.toCharArray(); - final int charLength = chars.length; - final byte[] bytes = new byte[charLength / 2]; + /** + * Returns the decoded byte array representation of str. + * @param str Hex encoded string to decode. + * @return Decoded byte array representation of str. + */ + public static byte[] fromHexToByteArray(String str) { + if (str == null || str.length() == 0 || str.length() % 2 != 0) { + return null; + } + + final char[] chars = str.toCharArray(); + final int charLength = chars.length; + final byte[] bytes = new byte[charLength / 2]; - for (int i = 0; i < bytes.length; i++) { - bytes[i] = - (byte)(((getIndex(chars[i * 2]) << 4) & 0xF0) | (getIndex(chars[i * 2 + 1]) & 0x0F)); + for (int i = 0; i < bytes.length; i++) { + bytes[i] = + (byte) (((getIndex(chars[i * 2]) << 4) & 0xF0) + | (getIndex(chars[i * 2 + 1]) & 0x0F)); + } + return bytes; } - return bytes; - } - private static int getIndex(char c) { - for (int i = 0; i < HEX_ARRAY.length; i++) { - if (HEX_ARRAY[i] == c) { - return i; - } + private static int getIndex(char c) { + for (int i = 0; i < HEX_UPPERCASE_ARRAY.length; i++) { + if (HEX_UPPERCASE_ARRAY[i] == c || HEX_LOWERCASE_ARRAY[i] == c) { + return i; + } + } + return -1; } - return -1; - } } diff --git a/core/java/android/util/ExceptionUtils.java b/core/java/android/util/ExceptionUtils.java index da7387fcae70..1a397b39ef3c 100644 --- a/core/java/android/util/ExceptionUtils.java +++ b/core/java/android/util/ExceptionUtils.java @@ -86,4 +86,16 @@ public class ExceptionUtils { while (t.getCause() != null) t = t.getCause(); return t; } -} + + /** + * Appends {@code cause} at the end of the causal chain of {@code t} + * + * @return {@code t} for convenience + */ + public static @NonNull Throwable appendCause(@NonNull Throwable t, @Nullable Throwable cause) { + if (cause != null) { + getRootCause(t).initCause(cause); + } + return t; + } +}
\ No newline at end of file diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index 410cdc6a9bf1..1ead0b49bbed 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -37,13 +37,13 @@ public class FeatureFlagUtils { private static final Map<String, String> DEFAULT_FLAGS; static { DEFAULT_FLAGS = new HashMap<>(); - DEFAULT_FLAGS.put("device_info_v2", "true"); DEFAULT_FLAGS.put("settings_connected_device_v2", "true"); DEFAULT_FLAGS.put("settings_battery_v2", "true"); DEFAULT_FLAGS.put("settings_battery_display_app_list", "false"); DEFAULT_FLAGS.put("settings_zone_picker_v2", "true"); - DEFAULT_FLAGS.put("settings_about_phone_v2", "false"); + DEFAULT_FLAGS.put("settings_about_phone_v2", "true"); DEFAULT_FLAGS.put("settings_bluetooth_while_driving", "false"); + DEFAULT_FLAGS.put("settings_data_usage_v2", "false"); } /** diff --git a/core/java/android/util/MemoryIntArray.java b/core/java/android/util/MemoryIntArray.java index 597089235e6b..bf335196edef 100644 --- a/core/java/android/util/MemoryIntArray.java +++ b/core/java/android/util/MemoryIntArray.java @@ -16,18 +16,12 @@ package android.util; -import static android.os.Process.FIRST_APPLICATION_UID; - import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; -import android.os.Process; - -import com.android.internal.annotations.GuardedBy; - -import dalvik.system.CloseGuard; import libcore.io.IoUtils; +import dalvik.system.CloseGuard; import java.io.Closeable; import java.io.IOException; @@ -55,18 +49,13 @@ import java.util.UUID; */ public final class MemoryIntArray implements Parcelable, Closeable { private static final String TAG = "MemoryIntArray"; - private static final boolean DEBUG = Process.myUid() < FIRST_APPLICATION_UID; private static final int MAX_SIZE = 1024; - private final Object mLock = new Object(); private final CloseGuard mCloseGuard = CloseGuard.get(); private final boolean mIsOwner; private final long mMemoryAddr; - - /** Fd for the shared memory object, -1 when closed */ - @GuardedBy("mLock") private int mFd = -1; /** @@ -85,7 +74,6 @@ public final class MemoryIntArray implements Parcelable, Closeable { mFd = nativeCreate(name, size); mMemoryAddr = nativeOpen(mFd, mIsOwner); mCloseGuard.open("close"); - if (DEBUG) Log.i(TAG, "created " + getString()); } private MemoryIntArray(Parcel parcel) throws IOException { @@ -97,8 +85,6 @@ public final class MemoryIntArray implements Parcelable, Closeable { mFd = pfd.detachFd(); mMemoryAddr = nativeOpen(mFd, mIsOwner); mCloseGuard.open("close"); - - if (DEBUG) Log.i(TAG, "created from parcel " + getString()); } /** @@ -155,33 +141,13 @@ public final class MemoryIntArray implements Parcelable, Closeable { */ @Override public void close() throws IOException { - synchronized (mLock) { - if (!isClosed()) { - if (DEBUG) { - try { - throw new Exception(); - } catch (Exception here) { - Log.i(TAG, "closing " + getString(), here); - } - } - nativeClose(mFd, mMemoryAddr, mIsOwner); - mFd = -1; - mCloseGuard.close(); - } else { - try { - throw new Exception(); - } catch (Exception here) { - if (DEBUG) Log.i(TAG, getString() + " already closed", here); - } - } + if (!isClosed()) { + nativeClose(mFd, mMemoryAddr, mIsOwner); + mFd = -1; + mCloseGuard.close(); } } - private String getString() { - return this.getClass().getSimpleName() + "@" + System.identityHashCode(this) - + " mMemoryAddr=" + mMemoryAddr + " mFd=" + mFd; - } - /** * @return Whether this array is closed and shouldn't be used. */ @@ -196,9 +162,7 @@ public final class MemoryIntArray implements Parcelable, Closeable { mCloseGuard.warnIfOpen(); } - if (!isClosed()) { - IoUtils.closeQuietly(this); - } + IoUtils.closeQuietly(this); } finally { super.finalize(); } @@ -242,8 +206,7 @@ public final class MemoryIntArray implements Parcelable, Closeable { private void enforceNotClosed() { if (isClosed()) { - throw new IllegalStateException("cannot interact with a closed instance " - + getString()); + throw new IllegalStateException("cannot interact with a closed instance"); } } diff --git a/core/java/android/util/OWNERS b/core/java/android/util/OWNERS new file mode 100644 index 000000000000..86ed1229b28e --- /dev/null +++ b/core/java/android/util/OWNERS @@ -0,0 +1,2 @@ +per-file FeatureFlagUtils.java = sbasi@google.com +per-file FeatureFlagUtils.java = zhfan@google.com diff --git a/core/java/android/util/StatsLog.java b/core/java/android/util/StatsLog.java index 4805318385d5..3350f3e164bc 100644 --- a/core/java/android/util/StatsLog.java +++ b/core/java/android/util/StatsLog.java @@ -34,7 +34,7 @@ public final class StatsLog extends StatsLogInternal { */ public static boolean logStart(int label) { if (label >= 0 && label < 16) { - StatsLog.write(APP_HOOK, label, APP_HOOK__STATE__START); + StatsLog.write(APP_BREADCRUMB_REPORTED, label, APP_BREADCRUMB_REPORTED__STATE__START); return true; } return false; @@ -48,7 +48,7 @@ public final class StatsLog extends StatsLogInternal { */ public static boolean logStop(int label) { if (label >= 0 && label < 16) { - StatsLog.write(APP_HOOK, label, APP_HOOK__STATE__STOP); + StatsLog.write(APP_BREADCRUMB_REPORTED, label, APP_BREADCRUMB_REPORTED__STATE__STOP); return true; } return false; @@ -62,7 +62,8 @@ public final class StatsLog extends StatsLogInternal { */ public static boolean logEvent(int label) { if (label >= 0 && label < 16) { - StatsLog.write(APP_HOOK, label, APP_HOOK__STATE__UNSPECIFIED); + StatsLog.write(APP_BREADCRUMB_REPORTED, label, + APP_BREADCRUMB_REPORTED__STATE__UNSPECIFIED); return true; } return false; diff --git a/core/java/android/util/StatsManager.java b/core/java/android/util/StatsManager.java deleted file mode 100644 index 51fb18a95c4c..000000000000 --- a/core/java/android/util/StatsManager.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright 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.util; - -import android.Manifest; -import android.annotation.RequiresPermission; -import android.os.IBinder; -import android.os.IStatsManager; -import android.os.RemoteException; -import android.os.ServiceManager; - - -/* - * - * - * - * - * THIS ENTIRE FILE IS ONLY TEMPORARY TO PREVENT BREAKAGES OF DEPENDENCIES ON OLD APIS. - * The new StatsManager is to be found in android.app.StatsManager. - * TODO: Delete this file! - * - * - * - * - */ - - -/** - * API for StatsD clients to send configurations and retrieve data. - * - * @hide - */ -public class StatsManager { - IStatsManager mService; - private static final String TAG = "StatsManager"; - - /** - * Constructor for StatsManagerClient. - * - * @hide - */ - public StatsManager() { - } - - /** - * Temporary to prevent build failures. Will be deleted. - */ - @RequiresPermission(Manifest.permission.DUMP) - public boolean addConfiguration(String configKey, byte[] config, String pkg, String cls) { - synchronized (this) { - try { - IStatsManager service = getIStatsManagerLocked(); - if (service == null) { - Slog.d(TAG, "Failed to find statsd when adding configuration"); - return false; - } - return service.addConfiguration(Long.parseLong(configKey), config, pkg, cls); - } catch (RemoteException e) { - Slog.d(TAG, "Failed to connect to statsd when adding configuration"); - return false; - } - } - } - - /** - * Clients can send a configuration and simultaneously registers the name of a broadcast - * receiver that listens for when it should request data. - * - * @param configKey An arbitrary integer that allows clients to track the configuration. - * @param config Wire-encoded StatsDConfig proto that specifies metrics (and all - * dependencies eg, conditions and matchers). - * @param pkg The package name to receive the broadcast. - * @param cls The name of the class that receives the broadcast. - * @return true if successful - */ - @RequiresPermission(Manifest.permission.DUMP) - public boolean addConfiguration(long configKey, byte[] config, String pkg, String cls) { - synchronized (this) { - try { - IStatsManager service = getIStatsManagerLocked(); - if (service == null) { - Slog.d(TAG, "Failed to find statsd when adding configuration"); - return false; - } - return service.addConfiguration(configKey, config, pkg, cls); - } catch (RemoteException e) { - Slog.d(TAG, "Failed to connect to statsd when adding configuration"); - return false; - } - } - } - - /** - * Temporary to prevent build failures. Will be deleted. - */ - @RequiresPermission(Manifest.permission.DUMP) - public boolean removeConfiguration(String configKey) { - // To prevent breakages of old dependencies. - synchronized (this) { - try { - IStatsManager service = getIStatsManagerLocked(); - if (service == null) { - Slog.d(TAG, "Failed to find statsd when removing configuration"); - return false; - } - return service.removeConfiguration(Long.parseLong(configKey)); - } catch (RemoteException e) { - Slog.d(TAG, "Failed to connect to statsd when removing configuration"); - return false; - } - } - } - - /** - * Remove a configuration from logging. - * - * @param configKey Configuration key to remove. - * @return true if successful - */ - @RequiresPermission(Manifest.permission.DUMP) - public boolean removeConfiguration(long configKey) { - synchronized (this) { - try { - IStatsManager service = getIStatsManagerLocked(); - if (service == null) { - Slog.d(TAG, "Failed to find statsd when removing configuration"); - return false; - } - return service.removeConfiguration(configKey); - } catch (RemoteException e) { - Slog.d(TAG, "Failed to connect to statsd when removing configuration"); - return false; - } - } - } - - /** - * Temporary to prevent build failures. Will be deleted. - */ - @RequiresPermission(Manifest.permission.DUMP) - public byte[] getData(String configKey) { - // TODO: remove this and all other methods with String-based config keys. - // To prevent build breakages of dependencies. - synchronized (this) { - try { - IStatsManager service = getIStatsManagerLocked(); - if (service == null) { - Slog.d(TAG, "Failed to find statsd when getting data"); - return null; - } - return service.getData(Long.parseLong(configKey)); - } catch (RemoteException e) { - Slog.d(TAG, "Failed to connecto statsd when getting data"); - return null; - } - } - } - - /** - * Clients can request data with a binder call. This getter is destructive and also clears - * the retrieved metrics from statsd memory. - * - * @param configKey Configuration key to retrieve data from. - * @return Serialized ConfigMetricsReportList proto. Returns null on failure. - */ - @RequiresPermission(Manifest.permission.DUMP) - public byte[] getData(long configKey) { - synchronized (this) { - try { - IStatsManager service = getIStatsManagerLocked(); - if (service == null) { - Slog.d(TAG, "Failed to find statsd when getting data"); - return null; - } - return service.getData(configKey); - } catch (RemoteException e) { - Slog.d(TAG, "Failed to connecto statsd when getting data"); - return null; - } - } - } - - /** - * Clients can request metadata for statsd. Will contain stats across all configurations but not - * the actual metrics themselves (metrics must be collected via {@link #getData(String)}. - * This getter is not destructive and will not reset any metrics/counters. - * - * @return Serialized StatsdStatsReport proto. Returns null on failure. - */ - @RequiresPermission(Manifest.permission.DUMP) - public byte[] getMetadata() { - synchronized (this) { - try { - IStatsManager service = getIStatsManagerLocked(); - if (service == null) { - Slog.d(TAG, "Failed to find statsd when getting metadata"); - return null; - } - return service.getMetadata(); - } catch (RemoteException e) { - Slog.d(TAG, "Failed to connecto statsd when getting metadata"); - return null; - } - } - } - - private class StatsdDeathRecipient implements IBinder.DeathRecipient { - @Override - public void binderDied() { - synchronized (this) { - mService = null; - } - } - } - - private IStatsManager getIStatsManagerLocked() throws RemoteException { - if (mService != null) { - return mService; - } - mService = IStatsManager.Stub.asInterface(ServiceManager.getService("stats")); - if (mService != null) { - mService.asBinder().linkToDeath(new StatsdDeathRecipient(), 0); - } - return mService; - } -} diff --git a/core/java/android/util/XmlPullAttributes.java b/core/java/android/util/XmlPullAttributes.java index 6c8bb39772f3..cb35eb5c12a3 100644 --- a/core/java/android/util/XmlPullAttributes.java +++ b/core/java/android/util/XmlPullAttributes.java @@ -34,6 +34,10 @@ class XmlPullAttributes implements AttributeSet { return mParser.getAttributeCount(); } + public String getAttributeNamespace (int index) { + return mParser.getAttributeNamespace(index); + } + public String getAttributeName(int index) { return mParser.getAttributeName(index); } diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java index a61c8c1dba68..0b72c22d4e27 100644 --- a/core/java/android/view/DisplayCutout.java +++ b/core/java/android/view/DisplayCutout.java @@ -52,6 +52,16 @@ public final class DisplayCutout { private static final String TAG = "DisplayCutout"; private static final String DP_MARKER = "@dp"; + /** + * Category for overlays that allow emulating a display cutout on devices that don't have + * one. + * + * @see android.content.om.IOverlayManager + * @hide + */ + public static final String EMULATION_OVERLAY_CATEGORY = + "com.android.internal.display_cutout_emulation"; + private static final Rect ZERO_RECT = new Rect(); private static final Region EMPTY_REGION = new Region(); diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index 37e9815c93c5..7251b71ac35d 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -20,6 +20,7 @@ import static android.view.DisplayInfoProto.APP_HEIGHT; import static android.view.DisplayInfoProto.APP_WIDTH; import static android.view.DisplayInfoProto.LOGICAL_HEIGHT; import static android.view.DisplayInfoProto.LOGICAL_WIDTH; +import static android.view.DisplayInfoProto.NAME; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; @@ -685,6 +686,7 @@ public final class DisplayInfo implements Parcelable { protoOutputStream.write(LOGICAL_HEIGHT, logicalHeight); protoOutputStream.write(APP_WIDTH, appWidth); protoOutputStream.write(APP_HEIGHT, appHeight); + protoOutputStream.write(NAME, name); protoOutputStream.end(token); } diff --git a/core/java/android/view/IRecentsAnimationRunner.aidl b/core/java/android/view/IRecentsAnimationRunner.aidl index ea6226b3ea69..69973e6d367a 100644 --- a/core/java/android/view/IRecentsAnimationRunner.aidl +++ b/core/java/android/view/IRecentsAnimationRunner.aidl @@ -16,6 +16,7 @@ package android.view; +import android.graphics.Rect; import android.view.RemoteAnimationTarget; import android.view.IRecentsAnimationController; @@ -28,15 +29,26 @@ import android.view.IRecentsAnimationController; oneway interface IRecentsAnimationRunner { /** - * Called when the system is ready for the handler to start animating all the visible tasks. + * Deprecated, to be removed once Launcher updates */ void onAnimationStart(in IRecentsAnimationController controller, - in RemoteAnimationTarget[] apps); + in RemoteAnimationTarget[] apps) = 0; /** * Called when the system needs to cancel the current animation. This can be due to the * wallpaper not drawing in time, or the handler not finishing the animation within a predefined * amount of time. */ - void onAnimationCanceled(); + void onAnimationCanceled() = 1; + + /** + * Called when the system is ready for the handler to start animating all the visible tasks. + * + * @param homeContentInsets The current home app content insets + * @param minimizedHomeBounds Specifies the bounds of the minimized home app, will be + * {@code null} if the device is not currently in split screen + */ + void onAnimationStart_New(in IRecentsAnimationController controller, + in RemoteAnimationTarget[] apps, in Rect homeContentInsets, + in Rect minimizedHomeBounds) = 2; } diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java index c28c3894482d..facf575872ed 100644 --- a/core/java/android/view/RemoteAnimationTarget.java +++ b/core/java/android/view/RemoteAnimationTarget.java @@ -79,6 +79,11 @@ public class RemoteAnimationTarget implements Parcelable { public final Rect clipRect; /** + * The insets of the main app window. + */ + public final Rect contentInsets; + + /** * The index of the element in the tree in prefix order. This should be used for z-layering * to preserve original z-layer order in the hierarchy tree assuming no "boosting" needs to * happen. @@ -105,13 +110,14 @@ public class RemoteAnimationTarget implements Parcelable { public final WindowConfiguration windowConfiguration; public RemoteAnimationTarget(int taskId, int mode, SurfaceControl leash, boolean isTranslucent, - Rect clipRect, int prefixOrderIndex, Point position, Rect sourceContainerBounds, - WindowConfiguration windowConfig) { + Rect clipRect, Rect contentInsets, int prefixOrderIndex, Point position, + Rect sourceContainerBounds, WindowConfiguration windowConfig) { this.mode = mode; this.taskId = taskId; this.leash = leash; this.isTranslucent = isTranslucent; this.clipRect = new Rect(clipRect); + this.contentInsets = new Rect(contentInsets); this.prefixOrderIndex = prefixOrderIndex; this.position = new Point(position); this.sourceContainerBounds = new Rect(sourceContainerBounds); @@ -124,6 +130,7 @@ public class RemoteAnimationTarget implements Parcelable { leash = in.readParcelable(null); isTranslucent = in.readBoolean(); clipRect = in.readParcelable(null); + contentInsets = in.readParcelable(null); prefixOrderIndex = in.readInt(); position = in.readParcelable(null); sourceContainerBounds = in.readParcelable(null); @@ -142,6 +149,7 @@ public class RemoteAnimationTarget implements Parcelable { dest.writeParcelable(leash, 0 /* flags */); dest.writeBoolean(isTranslucent); dest.writeParcelable(clipRect, 0 /* flags */); + dest.writeParcelable(contentInsets, 0 /* flags */); dest.writeInt(prefixOrderIndex); dest.writeParcelable(position, 0 /* flags */); dest.writeParcelable(sourceContainerBounds, 0 /* flags */); diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java index c4a716011765..d26a2f643ed4 100644 --- a/core/java/android/view/RenderNodeAnimator.java +++ b/core/java/android/view/RenderNodeAnimator.java @@ -158,7 +158,7 @@ public class RenderNodeAnimator extends Animator { } private void applyInterpolator() { - if (mInterpolator == null) return; + if (mInterpolator == null || mNativePtr == null) return; long ni; if (isNativeInterpolator(mInterpolator)) { diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 370c97e37262..e50d40ef098f 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -331,6 +331,7 @@ public final class ThreadedRenderer { private static final int FLAG_DUMP_FRAMESTATS = 1 << 0; private static final int FLAG_DUMP_RESET = 1 << 1; + private static final int FLAG_DUMP_ALL = FLAG_DUMP_FRAMESTATS; @IntDef(flag = true, prefix = { "FLAG_DUMP_" }, value = { FLAG_DUMP_FRAMESTATS, @@ -636,7 +637,10 @@ public final class ThreadedRenderer { */ void dumpGfxInfo(PrintWriter pw, FileDescriptor fd, String[] args) { pw.flush(); - int flags = 0; + // If there's no arguments, eg 'dumpsys gfxinfo', then dump everything. + // If there's a targetted package, eg 'dumpsys gfxinfo com.android.systemui', then only + // dump the summary information + int flags = (args == null || args.length == 0) ? FLAG_DUMP_ALL : 0; for (int i = 0; i < args.length; i++) { switch (args[i]) { case "framestats": @@ -645,6 +649,9 @@ public final class ThreadedRenderer { case "reset": flags |= FLAG_DUMP_RESET; break; + case "-a": // magic option passed when dumping a bugreport. + flags = FLAG_DUMP_ALL; + break; } } nDumpProfileInfo(mNativeProxy, fd, flags); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 79fc13424bee..3ff3c97620f0 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -17,6 +17,7 @@ package android.view; import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED; + import static java.lang.Math.max; import android.animation.AnimatorInflater; @@ -906,6 +907,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ private static boolean sThrowOnInvalidFloatProperties; + /** + * Prior to P, {@code #startDragAndDrop} accepts a builder which produces an empty drag shadow. + * Currently zero size SurfaceControl cannot be created thus we create a dummy 1x1 surface + * instead. + */ + private static boolean sAcceptZeroSizeDragShadow; + /** @hide */ @IntDef({NOT_FOCUSABLE, FOCUSABLE, FOCUSABLE_AUTO}) @Retention(RetentionPolicy.SOURCE) @@ -4798,6 +4806,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Canvas.sCompatibilityRestore = targetSdkVersion < Build.VERSION_CODES.M; Canvas.sCompatibilitySetBitmap = targetSdkVersion < Build.VERSION_CODES.O; + Canvas.setCompatibilityVersion(targetSdkVersion); // In M and newer, our widgets can pass a "hint" value in the size // for UNSPECIFIED MeasureSpecs. This lets child views of scrolling containers @@ -4840,6 +4849,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, sAlwaysAssignFocus = targetSdkVersion < Build.VERSION_CODES.P; + sAcceptZeroSizeDragShadow = targetSdkVersion < Build.VERSION_CODES.P; + sCompatibilityDone = true; } } @@ -7261,7 +7272,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // becomes true where it should issue notifyViewEntered(). afm.notifyViewEntered(this); } - } else if (!isFocused()) { + } else if (!enter && !isFocused()) { afm.notifyViewExited(this); } } @@ -7297,6 +7308,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return mAccessibilityPaneTitle; } + private boolean isAccessibilityPane() { + return !TextUtils.isEmpty(mAccessibilityPaneTitle); + } + /** * Sends an accessibility event of the given type. If accessibility is * not enabled this method has no effect. The default implementation calls @@ -7860,6 +7875,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, structure.setAutofillHints(getAutofillHints()); structure.setAutofillValue(getAutofillValue()); } + structure.setImportantForAutofill(getImportantForAutofill()); } int ignoredParentLeft = 0; @@ -7994,6 +8010,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * <li>Call {@link * android.view.autofill.AutofillManager#notifyViewVisibilityChanged(View, int, boolean)} * when the visibility of a virtual child changed. + * <li>Call + * {@link android.view.autofill.AutofillManager#notifyViewClicked(View, int)} when a virtual + * child is clicked. * <li>Call {@link AutofillManager#commit()} when the autofill context of the view structure * changed and the current context should be committed (for example, when the user tapped * a {@code SUBMIT} button in an HTML page). @@ -8355,6 +8374,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } + // If the app developer explicitly set hints for it, it's important. + if (getAutofillHints() != null) { + return true; + } + // Otherwise, assume it's not important... return false; } @@ -8378,7 +8402,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityNodeProvider provider, AccessibilityNodeInfo info, boolean forAutofill) { structure.setId(AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId()), - null, null, null); + null, null, info.getViewIdResourceName()); Rect rect = structure.getTempRect(); info.getBoundsInParent(rect); structure.setDimens(rect.left, rect.top, 0, 0, rect.width(), rect.height()); @@ -8418,6 +8442,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, CharSequence cname = info.getClassName(); structure.setClassName(cname != null ? cname.toString() : null); structure.setContentDescription(info.getContentDescription()); + if (forAutofill) { + final int maxTextLength = info.getMaxTextLength(); + if (maxTextLength != -1) { + structure.setMaxTextLength(maxTextLength); + } + structure.setHint(info.getHintText()); + } if ((info.getText() != null || info.getError() != null)) { structure.setText(info.getText(), info.getTextSelectionStart(), info.getTextSelectionEnd()); @@ -8428,7 +8459,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final AutofillValue autofillValue = AutofillValue.forText(structure.getText()); structure.setAutofillValue(autofillValue); if (info.isPassword()) { - structure.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD); + structure.setInputType(InputType.TYPE_CLASS_TEXT + | InputType.TYPE_TEXT_VARIATION_PASSWORD); } } else { structure.setDataIsSensitive(false); @@ -8778,6 +8810,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Computes whether this virtual autofill view is visible to the user. * + * <p><b>Note: </b>By default it returns {@code true}, but views providing a virtual hierarchy + * view must override it. + * * @return Whether the view is visible on the screen. */ public boolean isVisibleToUserForAutofill(int virtualId) { @@ -8790,7 +8825,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } } - return false; + return true; } /** @@ -11612,7 +11647,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return mode == IMPORTANT_FOR_ACCESSIBILITY_YES || isActionableForAccessibility() || hasListenersForAccessibility() || getAccessibilityNodeProvider() != null || getAccessibilityLiveRegion() != ACCESSIBILITY_LIVE_REGION_NONE - || (mAccessibilityPaneTitle != null); + || isAccessibilityPane(); } /** @@ -11709,18 +11744,26 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // Changes to views with a pane title count as window state changes, as the pane title // marks them as significant parts of the UI. - if (!TextUtils.isEmpty(getAccessibilityPaneTitle())) { - final AccessibilityEvent event = AccessibilityEvent.obtain(); - event.setEventType(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); - event.setContentChangeTypes(changeType); - onPopulateAccessibilityEvent(event); - if (mParent != null) { - try { - mParent.requestSendAccessibilityEvent(this, event); - } catch (AbstractMethodError e) { - Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() - + " does not fully implement ViewParent", e); + if ((changeType != AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE) + && isAccessibilityPane()) { + // If the pane isn't visible, content changed events are sufficient unless we're + // reporting that the view just disappeared + if ((getVisibility() == VISIBLE) + || (changeType == AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED)) { + final AccessibilityEvent event = AccessibilityEvent.obtain(); + event.setEventType(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); + event.setContentChangeTypes(changeType); + event.setSource(this); + onPopulateAccessibilityEvent(event); + if (mParent != null) { + try { + mParent.requestSendAccessibilityEvent(this, event); + } catch (AbstractMethodError e) { + Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() + + " does not fully implement ViewParent", e); + } } + return; } } @@ -14010,6 +14053,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } if (accessibilityEnabled) { + // If we're an accessibility pane and the visibility changed, we already have sent + // a state change, so we really don't need to report other changes. + if (isAccessibilityPane()) { + changed &= ~VISIBILITY_MASK; + } if ((changed & FOCUSABLE) != 0 || (changed & VISIBILITY_MASK) != 0 || (changed & CLICKABLE) != 0 || (changed & LONG_CLICKABLE) != 0 || (changed & CONTEXT_CLICKABLE) != 0) { @@ -23619,8 +23667,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * constructor variant is only useful when the {@link #onProvideShadowMetrics(Point, Point)} * and {@link #onDrawShadow(Canvas)} methods are also overridden in order * to supply the drag shadow's dimensions and appearance without - * reference to any View object. If they are not overridden, then the result is an - * invisible drag shadow. + * reference to any View object. */ public DragShadowBuilder() { mView = new WeakReference<View>(null); @@ -23774,6 +23821,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // Create 1x1 surface when zero surface size is specified because SurfaceControl.Builder // does not accept zero size surface. if (shadowSize.x == 0 || shadowSize.y == 0) { + if (!sAcceptZeroSizeDragShadow) { + throw new IllegalStateException("Drag shadow dimensions must be positive"); + } shadowSize.x = 1; shadowSize.y = 1; } diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index b09934e3e55f..276f50a51e66 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -21,7 +21,7 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; -import android.graphics.Point; +import android.graphics.Picture; import android.graphics.Rect; import android.os.Debug; import android.os.Handler; @@ -1782,27 +1782,18 @@ public class ViewDebug { * @hide */ public static class HardwareCanvasProvider implements CanvasProvider { - - private View mView; - private Point mSize; - private RenderNode mNode; - private DisplayListCanvas mCanvas; + private Picture mPicture; @Override public Canvas getCanvas(View view, int width, int height) { - mView = view; - mSize = new Point(width, height); - mNode = RenderNode.create("ViewDebug", mView); - mNode.setLeftTopRightBottom(0, 0, width, height); - mNode.setClipToBounds(false); - mCanvas = mNode.start(width, height); - return mCanvas; + mPicture = new Picture(); + return mPicture.beginRecording(width, height); } @Override public Bitmap createBitmap() { - mNode.end(mCanvas); - return ThreadedRenderer.createHardwareBitmap(mNode, mSize.x, mSize.y); + mPicture.endRecording(); + return Bitmap.createBitmap(mPicture); } } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 273f097edc63..8e60a720f729 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -3971,15 +3971,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } /** - * Layout debugging code which draws rectangles around layout params. - * - * <p>This function is called automatically when the developer setting is enabled.<p/> - * - * <p>It is strongly advised to only call this function from debug builds as there is - * a risk of leaking unwanted layout information.<p/> - * - * @param canvas the canvas on which to draw - * @param paint the paint used to draw through + * @hide */ protected void onDebugDrawMargins(Canvas canvas, Paint paint) { for (int i = 0; i < getChildCount(); i++) { @@ -3989,19 +3981,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } /** - * Layout debugging code which draws rectangles around: - * <ul> - * <li>optical bounds<li/> - * <li>margins<li/> - * <li>clip bounds<li/> - * <ul/> - * - * <p>This function is called automatically when the developer setting is enabled.<p/> - * - * <p>It is strongly advised to only call this function from debug builds as there is - * a risk of leaking unwanted layout information.<p/> - * - * @param canvas the canvas on which to draw + * @hide */ protected void onDebugDraw(Canvas canvas) { Paint paint = getDebugPaint(); @@ -7732,14 +7712,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager /** * Use {@code canvas} to draw suitable debugging annotations for these LayoutParameters. * - * <p>This function is called automatically when the developer setting is enabled.<p/> - * - * <p>It is strongly advised to only call this function from debug builds as there is - * a risk of leaking unwanted layout information.<p/> - * * @param view the view that contains these layout parameters * @param canvas the canvas on which to draw - * @param paint the paint used to draw through + * + * @hide */ public void onDebugDraw(View view, Canvas canvas, Paint paint) { } @@ -8243,6 +8219,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return ((mMarginFlags & LAYOUT_DIRECTION_MASK) == View.LAYOUT_DIRECTION_RTL); } + /** + * @hide + */ @Override public void onDebugDraw(View view, Canvas canvas, Paint paint) { Insets oi = isLayoutModeOptical(view.mParent) ? view.getOpticalInsets() : Insets.NONE; diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java index 1d94abeb51a2..3f7ab2aed4af 100644 --- a/core/java/android/view/ViewStructure.java +++ b/core/java/android/view/ViewStructure.java @@ -23,6 +23,8 @@ import android.graphics.Rect; import android.os.Bundle; import android.os.LocaleList; import android.util.Pair; +import android.view.View.AutofillImportance; +import android.view.ViewStructure.HtmlInfo; import android.view.autofill.AutofillId; import android.view.autofill.AutofillValue; @@ -347,6 +349,12 @@ public abstract class ViewStructure { public abstract void setAutofillOptions(CharSequence[] options); /** + * Sets the {@link View#setImportantForAutofill(int) importantForAutofill mode} of the + * view associated with this node. + */ + public void setImportantForAutofill(@AutofillImportance int mode) {} + + /** * Sets the {@link android.text.InputType} bits of this node. * * @param inputType inputType bits as defined by {@link android.text.InputType}. diff --git a/core/java/android/view/WindowInfo.java b/core/java/android/view/WindowInfo.java index bb9e391ddcb4..7bae28a4e817 100644 --- a/core/java/android/view/WindowInfo.java +++ b/core/java/android/view/WindowInfo.java @@ -21,6 +21,7 @@ import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; import android.util.Pools; +import android.view.accessibility.AccessibilityNodeInfo; import java.util.ArrayList; import java.util.List; @@ -46,7 +47,7 @@ public class WindowInfo implements Parcelable { public final Rect boundsInScreen = new Rect(); public List<IBinder> childTokens; public CharSequence title; - public int accessibilityIdOfAnchor = View.NO_ID; + public long accessibilityIdOfAnchor = AccessibilityNodeInfo.UNDEFINED_NODE_ID; public boolean inPictureInPicture; private WindowInfo() { @@ -105,7 +106,7 @@ public class WindowInfo implements Parcelable { parcel.writeInt(focused ? 1 : 0); boundsInScreen.writeToParcel(parcel, flags); parcel.writeCharSequence(title); - parcel.writeInt(accessibilityIdOfAnchor); + parcel.writeLong(accessibilityIdOfAnchor); parcel.writeInt(inPictureInPicture ? 1 : 0); if (childTokens != null && !childTokens.isEmpty()) { @@ -142,7 +143,7 @@ public class WindowInfo implements Parcelable { focused = (parcel.readInt() == 1); boundsInScreen.readFromParcel(parcel); title = parcel.readCharSequence(); - accessibilityIdOfAnchor = parcel.readInt(); + accessibilityIdOfAnchor = parcel.readLong(); inPictureInPicture = (parcel.readInt() == 1); final boolean hasChildren = (parcel.readInt() == 1); diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 1c5e87197750..c0a966602b0a 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -63,6 +63,7 @@ import android.os.Parcelable; import android.text.TextUtils; import android.util.Log; import android.util.proto.ProtoOutputStream; +import android.view.accessibility.AccessibilityNodeInfo; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -2344,7 +2345,7 @@ public interface WindowManager extends ViewManager { * * @hide */ - public int accessibilityIdOfAnchor = -1; + public long accessibilityIdOfAnchor = AccessibilityNodeInfo.UNDEFINED_NODE_ID; /** * The window title isn't kept in sync with what is displayed in the title bar, so we @@ -2538,7 +2539,7 @@ public interface WindowManager extends ViewManager { out.writeInt(hasManualSurfaceInsets ? 1 : 0); out.writeInt(preservePreviousSurfaceInsets ? 1 : 0); out.writeInt(needsMenuKey); - out.writeInt(accessibilityIdOfAnchor); + out.writeLong(accessibilityIdOfAnchor); TextUtils.writeToParcel(accessibilityTitle, out, parcelableFlags); out.writeInt(mColorMode); out.writeLong(hideTimeoutMilliseconds); @@ -2594,7 +2595,7 @@ public interface WindowManager extends ViewManager { hasManualSurfaceInsets = in.readInt() != 0; preservePreviousSurfaceInsets = in.readInt() != 0; needsMenuKey = in.readInt(); - accessibilityIdOfAnchor = in.readInt(); + accessibilityIdOfAnchor = in.readLong(); accessibilityTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); mColorMode = in.readInt(); hideTimeoutMilliseconds = in.readLong(); diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index 191c27073782..dee267dfea26 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -774,8 +774,9 @@ public final class AccessibilityManager { */ public void removeAccessibilityServicesStateChangeListener( @NonNull AccessibilityServicesStateChangeListener listener) { - // Final CopyOnWriteArrayList - no lock needed. - mServicesStateChangeListeners.remove(listener); + synchronized (mLock) { + mServicesStateChangeListeners.remove(listener); + } } /** diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 23e7d6191276..417a72530b72 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -3173,6 +3173,15 @@ public class AccessibilityNodeInfo implements Parcelable { */ @Override public void writeToParcel(Parcel parcel, int flags) { + writeToParcelNoRecycle(parcel, flags); + // Since instances of this class are fetched via synchronous i.e. blocking + // calls in IPCs we always recycle as soon as the instance is marshaled. + recycle(); + } + + /** @hide */ + @TestApi + public void writeToParcelNoRecycle(Parcel parcel, int flags) { // Write bit set of indices of fields with values differing from default long nonDefaultFields = 0; int fieldIndex = 0; // index of the current field @@ -3406,10 +3415,6 @@ public class AccessibilityNodeInfo implements Parcelable { + " vs " + fieldIndex); } } - - // Since instances of this class are fetched via synchronous i.e. blocking - // calls in IPCs we always recycle as soon as the instance is marshaled. - recycle(); } /** @@ -3557,7 +3562,7 @@ public class AccessibilityNodeInfo implements Parcelable { if (isBitSet(nonDefaultFields, fieldIndex++)) { mContentDescription = parcel.readCharSequence(); } - if (isBitSet(nonDefaultFields, fieldIndex++)) mPaneTitle = parcel.readString(); + if (isBitSet(nonDefaultFields, fieldIndex++)) mPaneTitle = parcel.readCharSequence(); if (isBitSet(nonDefaultFields, fieldIndex++)) mTooltipText = parcel.readCharSequence(); if (isBitSet(nonDefaultFields, fieldIndex++)) mViewIdResourceName = parcel.readString(); diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java index 990fbdb019d6..29f8442b2b46 100644 --- a/core/java/android/view/animation/AnimationUtils.java +++ b/core/java/android/view/animation/AnimationUtils.java @@ -18,6 +18,7 @@ package android.view.animation; import android.annotation.AnimRes; import android.annotation.InterpolatorRes; +import android.annotation.TestApi; import android.content.Context; import android.content.res.Resources; import android.content.res.Resources.NotFoundException; @@ -58,14 +59,43 @@ public class AnimationUtils { } }; - /** @hide */ + /** + * Locks AnimationUtils{@link #currentAnimationTimeMillis()} to a fixed value for the current + * thread. This is used by {@link android.view.Choreographer} to ensure that all accesses + * during a vsync update are synchronized to the timestamp of the vsync. + * + * It is also exposed to tests to allow for rapid, flake-free headless testing. + * + * Must be followed by a call to {@link #unlockAnimationClock()} to allow time to + * progress. Failing to do this will result in stuck animations, scrolls, and flings. + * + * Note that time is not allowed to "rewind" and must perpetually flow forward. So the + * lock may fail if the time is in the past from a previously returned value, however + * time will be frozen for the duration of the lock. The clock is a thread-local, so + * ensure that {@link #lockAnimationClock(long)}, {@link #unlockAnimationClock()}, and + * {@link #currentAnimationTimeMillis()} are all called on the same thread. + * + * This is also not reference counted in any way. Any call to {@link #unlockAnimationClock()} + * will unlock the clock for everyone on the same thread. It is therefore recommended + * for tests to use their own thread to ensure that there is no collision with any existing + * {@link android.view.Choreographer} instance. + * + * @hide + * */ + @TestApi public static void lockAnimationClock(long vsyncMillis) { AnimationState state = sAnimationState.get(); state.animationClockLocked = true; state.currentVsyncTimeMillis = vsyncMillis; } - /** @hide */ + /** + * Frees the time lock set in place by {@link #lockAnimationClock(long)}. Must be called + * to allow the animation clock to self-update. + * + * @hide + */ + @TestApi public static void unlockAnimationClock() { sAnimationState.get().animationClockLocked = false; } diff --git a/core/java/android/view/autofill/AutofillId.java b/core/java/android/view/autofill/AutofillId.java index 5ce2421aac87..cb1d89c54d9a 100644 --- a/core/java/android/view/autofill/AutofillId.java +++ b/core/java/android/view/autofill/AutofillId.java @@ -38,6 +38,7 @@ public final class AutofillId implements Parcelable { } /** @hide */ + @TestApi public AutofillId(AutofillId parent, int virtualChildId) { mVirtual = true; mViewId = parent.mViewId; diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 63a9990cf839..7792fa640015 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -16,10 +16,15 @@ package android.view.autofill; +import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST; +import static android.view.autofill.Helper.sDebug; +import static android.view.autofill.Helper.sVerbose; + import android.accessibilityservice.AccessibilityServiceInfo; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresFeature; import android.annotation.SystemService; import android.content.ComponentName; import android.content.Context; @@ -47,13 +52,14 @@ import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeProvider; import android.view.accessibility.AccessibilityWindowInfo; + import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.ArrayUtils; import com.android.internal.util.Preconditions; + import org.xmlpull.v1.XmlPullParserException; -import sun.misc.Cleaner; import java.io.IOException; import java.io.PrintWriter; @@ -66,11 +72,8 @@ import java.util.Collections; import java.util.List; import java.util.Objects; -import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST; -import static android.view.autofill.Helper.sDebug; -import static android.view.autofill.Helper.sVerbose; - -// TODO: use java.lang.ref.Cleaner once Android supports Java 9 +//TODO: use java.lang.ref.Cleaner once Android supports Java 9 +import sun.misc.Cleaner; /** * The {@link AutofillManager} provides ways for apps and custom views to integrate with the @@ -133,6 +136,7 @@ import static android.view.autofill.Helper.sVerbose; * <p>It is safe to call into its methods from any thread. */ @SystemService(Context.AUTOFILL_MANAGER_SERVICE) +@RequiresFeature(PackageManager.FEATURE_AUTOFILL) public final class AutofillManager { private static final String TAG = "AutofillManager"; @@ -353,6 +357,13 @@ public final class AutofillManager { @GuardedBy("mLock") @Nullable private ArraySet<AutofillId> mFillableIds; + /** + * Views that were already "entered" - if they're entered again when the session is not active, + * they're ignored + * */ + @GuardedBy("mLock") + @Nullable private ArraySet<AutofillId> mEnteredIds; + /** If set, session is commited when the field is clicked. */ @GuardedBy("mLock") @Nullable private AutofillId mSaveTriggerId; @@ -616,10 +627,9 @@ public final class AutofillManager { /** * @hide */ - public boolean isCompatibilityModeEnabled() { - synchronized (mLock) { - return mCompatibilityBridge != null; - } + @GuardedBy("mLock") + public boolean isCompatibilityModeEnabledLocked() { + return mCompatibilityBridge != null; } /** @@ -708,17 +718,30 @@ public final class AutofillManager { notifyViewEntered(view, 0); } - private boolean shouldIgnoreViewEnteredLocked(@NonNull View view, int flags) { + @GuardedBy("mLock") + private boolean shouldIgnoreViewEnteredLocked(@NonNull AutofillId id, int flags) { if (isDisabledByServiceLocked()) { if (sVerbose) { - Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + view - + ") on state " + getStateAsStringLocked()); + Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id + + ") on state " + getStateAsStringLocked() + " because disabled by svc"); } return true; } - if (sVerbose && isFinishedLocked()) { - Log.v(TAG, "not ignoring notifyViewEntered(flags=" + flags + ", view=" + view - + ") on state " + getStateAsStringLocked()); + if (isFinishedLocked()) { + // Session already finished: ignore if automatic request and view already entered + if ((flags & FLAG_MANUAL_REQUEST) == 0 && mEnteredIds != null + && mEnteredIds.contains(id)) { + if (sVerbose) { + Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id + + ") on state " + getStateAsStringLocked() + + " because view was already entered: " + mEnteredIds); + } + return true; + } + } + if (sVerbose) { + Log.v(TAG, "not ignoring notifyViewEntered(flags=" + flags + ", view=" + id + + ", state " + getStateAsStringLocked() + ", enteredIds=" + mEnteredIds); } return false; } @@ -748,8 +771,10 @@ public final class AutofillManager { } /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */ + @GuardedBy("mLock") private AutofillCallback notifyViewEnteredLocked(@NonNull View view, int flags) { - if (shouldIgnoreViewEnteredLocked(view, flags)) return null; + final AutofillId id = getAutofillId(view); + if (shouldIgnoreViewEnteredLocked(id, flags)) return null; AutofillCallback callback = null; @@ -762,7 +787,6 @@ public final class AutofillManager { } else { // don't notify entered when Activity is already in background if (!isClientDisablingEnterExitEvent()) { - final AutofillId id = getAutofillId(view); final AutofillValue value = view.getAutofillValue(); if (!isActiveLocked()) { @@ -772,6 +796,7 @@ public final class AutofillManager { // Update focus on existing session. updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags); } + addEnteredIdLocked(id); } } return callback; @@ -791,6 +816,7 @@ public final class AutofillManager { } } + @GuardedBy("mLock") void notifyViewExitedLocked(@NonNull View view) { ensureServiceClientAddedIfNeededLocked(); @@ -892,10 +918,12 @@ public final class AutofillManager { } /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */ + @GuardedBy("mLock") private AutofillCallback notifyViewEnteredLocked(View view, int virtualId, Rect bounds, int flags) { + final AutofillId id = getAutofillId(view, virtualId); AutofillCallback callback = null; - if (shouldIgnoreViewEnteredLocked(view, flags)) return callback; + if (shouldIgnoreViewEnteredLocked(id, flags)) return callback; ensureServiceClientAddedIfNeededLocked(); @@ -906,8 +934,6 @@ public final class AutofillManager { } else { // don't notify entered when Activity is already in background if (!isClientDisablingEnterExitEvent()) { - final AutofillId id = getAutofillId(view, virtualId); - if (!isActiveLocked()) { // Starts new session. startSessionLocked(id, bounds, null, flags); @@ -915,11 +941,20 @@ public final class AutofillManager { // Update focus on existing session. updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags); } + addEnteredIdLocked(id); } } return callback; } + @GuardedBy("mLock") + private void addEnteredIdLocked(@NonNull AutofillId id) { + if (mEnteredIds == null) { + mEnteredIds = new ArraySet<>(1); + } + mEnteredIds.add(id); + } + /** * Called when a virtual view that supports autofill is exited. * @@ -935,6 +970,7 @@ public final class AutofillManager { } } + @GuardedBy("mLock") private void notifyViewExitedLocked(@NonNull View view, int virtualId) { ensureServiceClientAddedIfNeededLocked(); @@ -985,9 +1021,9 @@ public final class AutofillManager { } if (!mEnabled || !isActiveLocked()) { - if (sVerbose && mEnabled) { - Log.v(TAG, "notifyValueChanged(" + view + "): ignoring on state " - + getStateAsStringLocked()); + if (sVerbose) { + Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() + + "): ignoring on state " + getStateAsStringLocked()); } return; } @@ -1017,6 +1053,10 @@ public final class AutofillManager { } synchronized (mLock) { if (!mEnabled || !isActiveLocked()) { + if (sVerbose) { + Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() + ":" + virtualId + + "): ignoring on state " + getStateAsStringLocked()); + } return; } @@ -1025,18 +1065,35 @@ public final class AutofillManager { } } + /** + * Called to indicate a {@link View} is clicked. + * + * @param view view that has been clicked. + */ + public void notifyViewClicked(@NonNull View view) { + notifyViewClicked(view.getAutofillId()); + } /** - * Called when a {@link View} is clicked. Currently only used by views that should trigger save. + * Called to indicate a virtual view has been clicked. * - * @hide + * @param view the virtual view parent. + * @param virtualId id identifying the virtual child inside the parent view. */ - public void notifyViewClicked(View view) { - final AutofillId id = view.getAutofillId(); + public void notifyViewClicked(@NonNull View view, int virtualId) { + notifyViewClicked(getAutofillId(view, virtualId)); + } + private void notifyViewClicked(AutofillId id) { + if (!hasAutofillFeature()) { + return; + } if (sVerbose) Log.v(TAG, "notifyViewClicked(): id=" + id + ", trigger=" + mSaveTriggerId); synchronized (mLock) { + if (!mEnabled || !isActiveLocked()) { + return; + } if (mSaveTriggerId != null && mSaveTriggerId.equals(id)) { if (sDebug) Log.d(TAG, "triggering commit by click of " + id); commitLocked(); @@ -1051,16 +1108,16 @@ public final class AutofillManager { * * @hide */ - public void onActivityFinished() { + public void onActivityFinishing() { if (!hasAutofillFeature()) { return; } synchronized (mLock) { if (mSaveOnFinish) { - if (sDebug) Log.d(TAG, "Committing session on finish() as requested by service"); + if (sDebug) Log.d(TAG, "onActivityFinishing(): calling commitLocked()"); commitLocked(); } else { - if (sDebug) Log.d(TAG, "Cancelling session on finish() as requested by service"); + if (sDebug) Log.d(TAG, "onActivityFinishing(): calling cancelLocked()"); cancelLocked(); } } @@ -1081,11 +1138,13 @@ public final class AutofillManager { if (!hasAutofillFeature()) { return; } + if (sVerbose) Log.v(TAG, "commit() called by app"); synchronized (mLock) { commitLocked(); } } + @GuardedBy("mLock") private void commitLocked() { if (!mEnabled && !isActiveLocked()) { return; @@ -1114,6 +1173,7 @@ public final class AutofillManager { } } + @GuardedBy("mLock") private void cancelLocked() { if (!mEnabled && !isActiveLocked()) { return; @@ -1377,11 +1437,14 @@ public final class AutofillManager { return new AutofillId(parent.getAutofillViewId(), virtualId); } + @GuardedBy("mLock") private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds, @NonNull AutofillValue value, int flags) { if (sVerbose) { Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value - + ", flags=" + flags + ", state=" + getStateAsStringLocked()); + + ", flags=" + flags + ", state=" + getStateAsStringLocked() + + ", compatMode=" + isCompatibilityModeEnabledLocked() + + ", enteredIds=" + mEnteredIds); } if (mState != STATE_UNKNOWN && !isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) { if (sVerbose) { @@ -1392,11 +1455,12 @@ public final class AutofillManager { } try { final AutofillClient client = getClient(); - if (client == null) return; // NOTE: getClient() already logd it.. + if (client == null) return; // NOTE: getClient() already logged it.. mSessionId = mService.startSession(client.autofillClientGetActivityToken(), mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(), - mCallback != null, flags, client.autofillClientGetComponentName()); + mCallback != null, flags, client.autofillClientGetComponentName(), + isCompatibilityModeEnabledLocked()); if (mSessionId != NO_SESSION) { mState = STATE_ACTIVE; } @@ -1406,6 +1470,7 @@ public final class AutofillManager { } } + @GuardedBy("mLock") private void finishSessionLocked() { if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + getStateAsStringLocked()); @@ -1417,9 +1482,10 @@ public final class AutofillManager { throw e.rethrowFromSystemServer(); } - resetSessionLocked(); + resetSessionLocked(/* resetEnteredIds= */ true); } + @GuardedBy("mLock") private void cancelSessionLocked() { if (sVerbose) Log.v(TAG, "cancelSessionLocked(): " + getStateAsStringLocked()); @@ -1431,20 +1497,25 @@ public final class AutofillManager { throw e.rethrowFromSystemServer(); } - resetSessionLocked(); + resetSessionLocked(/* resetEnteredIds= */ true); } - private void resetSessionLocked() { + @GuardedBy("mLock") + private void resetSessionLocked(boolean resetEnteredIds) { mSessionId = NO_SESSION; mState = STATE_UNKNOWN; mTrackedViews = null; mFillableIds = null; mSaveTriggerId = null; + if (resetEnteredIds) { + mEnteredIds = null; + } } + @GuardedBy("mLock") private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action, int flags) { - if (sVerbose && action != ACTION_VIEW_EXITED) { + if (sVerbose) { Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value + ", action=" + action + ", flags=" + flags); } @@ -1459,7 +1530,7 @@ public final class AutofillManager { client.autofillClientGetActivityToken(), mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(), mCallback != null, flags, client.autofillClientGetComponentName(), - mSessionId, action); + mSessionId, action, isCompatibilityModeEnabledLocked()); if (newId != mSessionId) { if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId); mSessionId = newId; @@ -1476,6 +1547,7 @@ public final class AutofillManager { } } + @GuardedBy("mLock") private void ensureServiceClientAddedIfNeededLocked() { if (getClient() == null) { return; @@ -1613,7 +1685,7 @@ public final class AutofillManager { mEnabled = (flags & SET_STATE_FLAG_ENABLED) != 0; if (!mEnabled || (flags & SET_STATE_FLAG_RESET_SESSION) != 0) { // Reset the session state - resetSessionLocked(); + resetSessionLocked(/* resetEnteredIds= */ true); } if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) { // Reset connection to system @@ -1809,7 +1881,7 @@ public final class AutofillManager { private void setSessionFinished(int newState) { synchronized (mLock) { if (sVerbose) Log.v(TAG, "setSessionFinished(): from " + mState + " to " + newState); - resetSessionLocked(); + resetSessionLocked(/* resetEnteredIds= */ false); mState = newState; } } @@ -1937,13 +2009,16 @@ public final class AutofillManager { pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds); } pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds); + pw.print(pfx); pw.print("entered ids: "); pw.println(mEnteredIds); pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId); pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish); - pw.print(pfx); pw.print("compat mode enabled: "); pw.println(isCompatibilityModeEnabled()); + pw.print(pfx); pw.print("compat mode enabled: "); pw.println( + isCompatibilityModeEnabledLocked()); pw.print(pfx); pw.print("debug: "); pw.print(sDebug); pw.print(" verbose: "); pw.println(sVerbose); } + @GuardedBy("mLock") private String getStateAsStringLocked() { switch (mState) { case STATE_UNKNOWN: @@ -1961,14 +2036,17 @@ public final class AutofillManager { } } + @GuardedBy("mLock") private boolean isActiveLocked() { return mState == STATE_ACTIVE; } + @GuardedBy("mLock") private boolean isDisabledByServiceLocked() { return mState == STATE_DISABLED_BY_SERVICE; } + @GuardedBy("mLock") private boolean isFinishedLocked() { return mState == STATE_FINISHED; } @@ -2164,6 +2242,7 @@ public final class AutofillManager { AutofillValue.forText(node.getText())); } + @GuardedBy("mLock") private void updateTrackedViewsLocked() { if (mTrackedViews != null) { mTrackedViews.onVisibleForAutofillChangedLocked(); @@ -2273,6 +2352,7 @@ public final class AutofillManager { final boolean[] isVisible; if (client.autofillClientIsVisibleForAutofill()) { + if (sVerbose) Log.v(TAG, "client is visible, check tracked ids"); isVisible = client.autofillClientGetViewVisibility(trackedIds); } else { // All false @@ -2292,7 +2372,7 @@ public final class AutofillManager { } if (sVerbose) { - Log.v(TAG, "TrackedViews(trackedIds=" + trackedIds + "): " + Log.v(TAG, "TrackedViews(trackedIds=" + Arrays.toString(trackedIds) + "): " + " mVisibleTrackedIds=" + mVisibleTrackedIds + " mInvisibleTrackedIds=" + mInvisibleTrackedIds); } @@ -2308,6 +2388,7 @@ public final class AutofillManager { * @param id the id of the view/virtual view whose visibility changed. * @param isVisible visible if the view is visible in the view hierarchy. */ + @GuardedBy("mLock") void notifyViewVisibilityChangedLocked(@NonNull AutofillId id, boolean isVisible) { if (sDebug) { Log.d(TAG, "notifyViewVisibilityChangedLocked(): id=" + id + " isVisible=" @@ -2341,6 +2422,7 @@ public final class AutofillManager { * * @see AutofillClient#autofillClientIsVisibleForAutofill() */ + @GuardedBy("mLock") void onVisibleForAutofillChangedLocked() { // The visibility of the views might have changed while the client was not be visible, // hence update the visibility state for all views. @@ -2396,6 +2478,9 @@ public final class AutofillManager { } if (mVisibleTrackedIds == null) { + if (sVerbose) { + Log.v(TAG, "onVisibleForAutofillChangedLocked(): no more visible ids"); + } finishSessionLocked(); } } diff --git a/core/java/android/view/autofill/AutofillPopupWindow.java b/core/java/android/view/autofill/AutofillPopupWindow.java index e80fdd93542c..1da998d01ba3 100644 --- a/core/java/android/view/autofill/AutofillPopupWindow.java +++ b/core/java/android/view/autofill/AutofillPopupWindow.java @@ -46,6 +46,7 @@ public class AutofillPopupWindow extends PopupWindow { private final WindowPresenter mWindowPresenter; private WindowManager.LayoutParams mWindowLayoutParams; + private boolean mFullScreen; private final View.OnAttachStateChangeListener mOnAttachStateChangeListener = new View.OnAttachStateChangeListener() { @@ -104,12 +105,17 @@ public class AutofillPopupWindow extends PopupWindow { */ public void update(View anchor, int offsetX, int offsetY, int width, int height, Rect virtualBounds) { + mFullScreen = width == LayoutParams.MATCH_PARENT && height == LayoutParams.MATCH_PARENT; // If we are showing the popup for a virtual view we use a fake view which // delegates to the anchor but present itself with the same bounds as the // virtual view. This ensures that the location logic in popup works // symmetrically when the dropdown is below and above the anchor. final View actualAnchor; - if (virtualBounds != null) { + if (mFullScreen) { + offsetX = 0; + offsetY = 0; + actualAnchor = anchor; + } else if (virtualBounds != null) { final int[] mLocationOnScreen = new int[] {virtualBounds.left, virtualBounds.top}; actualAnchor = new View(anchor.getContext()) { @Override @@ -209,6 +215,17 @@ public class AutofillPopupWindow extends PopupWindow { } @Override + protected boolean findDropDownPosition(View anchor, LayoutParams outParams, + int xOffset, int yOffset, int width, int height, int gravity, boolean allowScroll) { + if (mFullScreen) { + // Do not patch LayoutParams if force full screen + return false; + } + return super.findDropDownPosition(anchor, outParams, xOffset, yOffset, + width, height, gravity, allowScroll); + } + + @Override public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) { if (sVerbose) { Log.v(TAG, "showAsDropDown(): anchor=" + anchor + ", xoff=" + xoff + ", yoff=" + yoff diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl index 001854716715..56f79abf6c19 100644 --- a/core/java/android/view/autofill/IAutoFillManager.aidl +++ b/core/java/android/view/autofill/IAutoFillManager.aidl @@ -38,7 +38,7 @@ interface IAutoFillManager { void removeClient(in IAutoFillManagerClient client, int userId); int startSession(IBinder activityToken, in IBinder appCallback, in AutofillId autoFillId, in Rect bounds, in AutofillValue value, int userId, boolean hasCallback, int flags, - in ComponentName componentName); + in ComponentName componentName, boolean compatMode); FillEventHistory getFillEventHistory(); boolean restoreSession(int sessionId, in IBinder activityToken, in IBinder appCallback); void updateSession(int sessionId, in AutofillId id, in Rect bounds, @@ -46,7 +46,7 @@ interface IAutoFillManager { int updateOrRestartSession(IBinder activityToken, in IBinder appCallback, in AutofillId autoFillId, in Rect bounds, in AutofillValue value, int userId, boolean hasCallback, int flags, in ComponentName componentName, int sessionId, - int action); + int action, boolean compatMode); void finishSession(int sessionId, int userId); void cancelSession(int sessionId, int userId); void setAuthenticationResult(in Bundle data, int sessionId, int authenticationId, int userId); diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 9de26a86f20f..a2280a4acd11 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -20,10 +20,12 @@ import static android.Manifest.permission.WRITE_SECURE_SETTINGS; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresFeature; import android.annotation.RequiresPermission; import android.annotation.SystemService; import android.annotation.TestApi; import android.content.Context; +import android.content.pm.PackageManager; import android.graphics.Rect; import android.inputmethodservice.InputMethodService; import android.net.Uri; @@ -213,6 +215,7 @@ import java.util.concurrent.TimeUnit; * </ul> */ @SystemService(Context.INPUT_METHOD_SERVICE) +@RequiresFeature(PackageManager.FEATURE_INPUT_METHODS) public final class InputMethodManager { static final boolean DEBUG = false; static final String TAG = "InputMethodManager"; diff --git a/core/java/android/view/textclassifier/SmartSelection.java b/core/java/android/view/textclassifier/SmartSelection.java index 8edf97ea0336..69c38ee4db4f 100644 --- a/core/java/android/view/textclassifier/SmartSelection.java +++ b/core/java/android/view/textclassifier/SmartSelection.java @@ -108,9 +108,9 @@ final class SmartSelection { } /** - * Returns the language of the model. + * Returns a comma separated list of locales supported by the model as BCP 47 tags. */ - public static String getLanguage(int fd) { + public static String getLanguages(int fd) { return nativeGetLanguage(fd); } diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java index af55dcd0ed72..cbc3828ba56e 100644 --- a/core/java/android/view/textclassifier/SystemTextClassifier.java +++ b/core/java/android/view/textclassifier/SystemTextClassifier.java @@ -121,6 +121,15 @@ final class SystemTextClassifier implements TextClassifier { return mFallback.generateLinks(text, options); } + /** + * @inheritDoc + */ + @Override + public int getMaxGenerateLinksTextLength() { + // TODO: retrieve this from the bound service. + return mFallback.getMaxGenerateLinksTextLength(); + } + private static final class TextSelectionCallback extends ITextSelectionCallback.Stub { final ResponseReceiver<TextSelection> mReceiver = new ResponseReceiver<>(); diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java index a6a2a945e774..8fe1d8fcc805 100644 --- a/core/java/android/view/textclassifier/TextClassification.java +++ b/core/java/android/view/textclassifier/TextClassification.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.content.Intent; +import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.drawable.BitmapDrawable; @@ -378,7 +379,7 @@ public final class TextClassification implements Parcelable { final List<Drawable> drawables = new ArrayList<>(bitmaps.size()); for (Bitmap bitmap : bitmaps) { if (bitmap != null) { - drawables.add(new BitmapDrawable(null, bitmap)); + drawables.add(new BitmapDrawable(Resources.getSystem(), bitmap)); } else { drawables.add(null); } @@ -681,7 +682,8 @@ public final class TextClassification implements Parcelable { private TextClassification(Parcel in) { mText = in.readString(); mPrimaryIcon = in.readInt() == 0 - ? null : new BitmapDrawable(null, Bitmap.CREATOR.createFromParcel(in)); + ? null + : new BitmapDrawable(Resources.getSystem(), Bitmap.CREATOR.createFromParcel(in)); mPrimaryLabel = in.readString(); mPrimaryIntent = in.readInt() == 0 ? null : Intent.CREATOR.createFromParcel(in); mPrimaryOnClickListener = null; // not parcelable diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java index 9f75c4a80ca2..2a62f23f19c7 100644 --- a/core/java/android/view/textclassifier/TextClassifier.java +++ b/core/java/android/view/textclassifier/TextClassifier.java @@ -276,9 +276,11 @@ public interface TextClassifier { * @param text the text to generate annotations for * @param options configuration for link generation * - * @throws IllegalArgumentException if text is null + * @throws IllegalArgumentException if text is null or the text is too long for the + * TextClassifier implementation. * * @see #generateLinks(CharSequence) + * @see #getMaxGenerateLinksTextLength() */ @WorkerThread default TextLinks generateLinks( @@ -299,9 +301,11 @@ public interface TextClassifier { * * @param text the text to generate annotations for * - * @throws IllegalArgumentException if text is null + * @throws IllegalArgumentException if text is null or the text is too long for the + * TextClassifier implementation. * * @see #generateLinks(CharSequence, TextLinks.Options) + * @see #getMaxGenerateLinksTextLength() */ @WorkerThread default TextLinks generateLinks(@NonNull CharSequence text) { @@ -309,6 +313,16 @@ public interface TextClassifier { } /** + * Returns the maximal length of text that can be processed by generateLinks. + * + * @see #generateLinks(CharSequence) + * @see #generateLinks(CharSequence, TextLinks.Options) + */ + default int getMaxGenerateLinksTextLength() { + return Integer.MAX_VALUE; + } + + /** * Returns a {@link Collection} of the entity types in the specified preset. * * @see #ENTITY_PRESET_ALL @@ -461,6 +475,15 @@ public interface TextClassifier { checkMainThread(allowInMainThread); } + /** + * @throws IllegalArgumentException if text is null; the text is too long or options is null + */ + public static void validate(@NonNull CharSequence text, int maxLength, + boolean allowInMainThread) { + validate(text, allowInMainThread); + Preconditions.checkArgumentInRange(text.length(), 0, maxLength, "text.length()"); + } + private static void checkMainThread(boolean allowInMainThread) { if (!allowInMainThread && Looper.myLooper() == Looper.getMainLooper()) { Slog.w(DEFAULT_LOG_TAG, "TextClassifier called on main thread"); diff --git a/core/java/android/view/textclassifier/TextClassifierConstants.java b/core/java/android/view/textclassifier/TextClassifierConstants.java index 00695b797cb3..efa69488521f 100644 --- a/core/java/android/view/textclassifier/TextClassifierConstants.java +++ b/core/java/android/view/textclassifier/TextClassifierConstants.java @@ -47,10 +47,19 @@ public final class TextClassifierConstants { "smart_selection_enabled_for_edit_text"; private static final String SMART_LINKIFY_ENABLED = "smart_linkify_enabled"; + private static final String SUGGEST_SELECTION_MAX_RANGE_LENGTH = + "suggest_selection_max_range_length"; + private static final String CLASSIFY_TEXT_MAX_RANGE_LENGTH = + "classify_text_max_range_length"; + private static final String GENERATE_LINKS_MAX_TEXT_LENGTH = + "generate_links_max_text_length"; private static final boolean SMART_SELECTION_DARK_LAUNCH_DEFAULT = false; private static final boolean SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT = true; private static final boolean SMART_LINKIFY_ENABLED_DEFAULT = true; + private static final int SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000; + private static final int CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000; + private static final int GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT = 100 * 1000; /** Default settings. */ static final TextClassifierConstants DEFAULT = new TextClassifierConstants(); @@ -58,11 +67,17 @@ public final class TextClassifierConstants { private final boolean mDarkLaunch; private final boolean mSuggestSelectionEnabledForEditableText; private final boolean mSmartLinkifyEnabled; + private final int mSuggestSelectionMaxRangeLength; + private final int mClassifyTextMaxRangeLength; + private final int mGenerateLinksMaxTextLength; private TextClassifierConstants() { mDarkLaunch = SMART_SELECTION_DARK_LAUNCH_DEFAULT; mSuggestSelectionEnabledForEditableText = SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT; mSmartLinkifyEnabled = SMART_LINKIFY_ENABLED_DEFAULT; + mSuggestSelectionMaxRangeLength = SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT; + mClassifyTextMaxRangeLength = CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT; + mGenerateLinksMaxTextLength = GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT; } private TextClassifierConstants(@Nullable String settings) { @@ -82,6 +97,15 @@ public final class TextClassifierConstants { mSmartLinkifyEnabled = parser.getBoolean( SMART_LINKIFY_ENABLED, SMART_LINKIFY_ENABLED_DEFAULT); + mSuggestSelectionMaxRangeLength = parser.getInt( + SUGGEST_SELECTION_MAX_RANGE_LENGTH, + SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT); + mClassifyTextMaxRangeLength = parser.getInt( + CLASSIFY_TEXT_MAX_RANGE_LENGTH, + CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT); + mGenerateLinksMaxTextLength = parser.getInt( + GENERATE_LINKS_MAX_TEXT_LENGTH, + GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT); } static TextClassifierConstants loadFromString(String settings) { @@ -99,4 +123,16 @@ public final class TextClassifierConstants { public boolean isSmartLinkifyEnabled() { return mSmartLinkifyEnabled; } + + public int getSuggestSelectionMaxRangeLength() { + return mSuggestSelectionMaxRangeLength; + } + + public int getClassifyTextMaxRangeLength() { + return mClassifyTextMaxRangeLength; + } + + public int getGenerateLinksMaxTextLength() { + return mGenerateLinksMaxTextLength; + } } diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java index 9f389ba0c140..795caffd04d5 100644 --- a/core/java/android/view/textclassifier/TextClassifierImpl.java +++ b/core/java/android/view/textclassifier/TextClassifierImpl.java @@ -27,8 +27,10 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.Bundle; import android.os.LocaleList; import android.os.ParcelFileDescriptor; +import android.os.UserManager; import android.provider.Browser; import android.provider.CalendarContract; import android.provider.ContactsContract; @@ -56,6 +58,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; +import java.util.StringJoiner; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -99,11 +102,9 @@ public final class TextClassifierImpl implements TextClassifier { private final Object mLock = new Object(); @GuardedBy("mLock") // Do not access outside this lock. - private Map<Locale, String> mModelFilePaths; + private List<ModelFile> mAllModelFiles; @GuardedBy("mLock") // Do not access outside this lock. - private Locale mLocale; - @GuardedBy("mLock") // Do not access outside this lock. - private int mVersion; + private ModelFile mModel; @GuardedBy("mLock") // Do not access outside this lock. private SmartSelection mSmartSelection; @@ -127,7 +128,9 @@ public final class TextClassifierImpl implements TextClassifier { @Nullable TextSelection.Options options) { Utils.validate(text, selectionStartIndex, selectionEndIndex, false /* allowInMainThread */); try { - if (text.length() > 0) { + final int rangeLength = selectionEndIndex - selectionStartIndex; + if (text.length() > 0 + && rangeLength <= getSettings().getSuggestSelectionMaxRangeLength()) { final LocaleList locales = (options == null) ? null : options.getDefaultLocales(); final boolean darkLaunchAllowed = options != null && options.isDarkLaunchAllowed(); final SmartSelection smartSelection = getSmartSelection(locales); @@ -182,7 +185,8 @@ public final class TextClassifierImpl implements TextClassifier { @Nullable TextClassification.Options options) { Utils.validate(text, startIndex, endIndex, false /* allowInMainThread */); try { - if (text.length() > 0) { + final int rangeLength = endIndex - startIndex; + if (text.length() > 0 && rangeLength <= getSettings().getClassifyTextMaxRangeLength()) { final String string = text.toString(); final LocaleList locales = (options == null) ? null : options.getDefaultLocales(); final Calendar refTime = (options == null) ? null : options.getReferenceTime(); @@ -206,7 +210,7 @@ public final class TextClassifierImpl implements TextClassifier { @Override public TextLinks generateLinks( @NonNull CharSequence text, @Nullable TextLinks.Options options) { - Utils.validate(text, false /* allowInMainThread */); + Utils.validate(text, getMaxGenerateLinksTextLength(), false /* allowInMainThread */); final String textString = text.toString(); final TextLinks.Builder builder = new TextLinks.Builder(textString); @@ -240,6 +244,12 @@ public final class TextClassifierImpl implements TextClassifier { return mFallback.generateLinks(text, options); } + /** @inheritDoc */ + @Override + public int getMaxGenerateLinksTextLength() { + return getSettings().getGenerateLinksMaxTextLength(); + } + @Override public Collection<String> getEntitiesForPreset(@TextClassifier.EntityPreset int entityPreset) { switch (entityPreset) { @@ -279,18 +289,18 @@ public final class TextClassifierImpl implements TextClassifier { private SmartSelection getSmartSelection(LocaleList localeList) throws FileNotFoundException { synchronized (mLock) { localeList = localeList == null ? LocaleList.getEmptyLocaleList() : localeList; - final Locale locale = findBestSupportedLocaleLocked(localeList); - if (locale == null) { - throw new FileNotFoundException("No file for null locale"); + final ModelFile bestModel = findBestModelLocked(localeList); + if (bestModel == null) { + throw new FileNotFoundException("No model for " + localeList.toLanguageTags()); } - if (mSmartSelection == null || !Objects.equals(mLocale, locale)) { + if (mSmartSelection == null || !Objects.equals(mModel, bestModel)) { + Log.d(DEFAULT_LOG_TAG, "Loading " + bestModel); destroySmartSelectionIfExistsLocked(); - final ParcelFileDescriptor fd = getFdLocked(locale); - final int modelFd = fd.getFd(); - mVersion = SmartSelection.getVersion(modelFd); - mSmartSelection = new SmartSelection(modelFd); + final ParcelFileDescriptor fd = ParcelFileDescriptor.open( + new File(bestModel.getPath()), ParcelFileDescriptor.MODE_READ_ONLY); + mSmartSelection = new SmartSelection(fd.getFd()); closeAndLogError(fd); - mLocale = locale; + mModel = bestModel; } return mSmartSelection; } @@ -298,74 +308,8 @@ public final class TextClassifierImpl implements TextClassifier { private String getSignature(String text, int start, int end) { synchronized (mLock) { - return DefaultLogger.createSignature(text, start, end, mContext, mVersion, mLocale); - } - } - - @GuardedBy("mLock") // Do not call outside this lock. - private ParcelFileDescriptor getFdLocked(Locale locale) throws FileNotFoundException { - ParcelFileDescriptor updateFd; - int updateVersion = -1; - try { - updateFd = ParcelFileDescriptor.open( - new File(UPDATED_MODEL_FILE_PATH), ParcelFileDescriptor.MODE_READ_ONLY); - if (updateFd != null) { - updateVersion = SmartSelection.getVersion(updateFd.getFd()); - } - } catch (FileNotFoundException e) { - updateFd = null; - } - ParcelFileDescriptor factoryFd; - int factoryVersion = -1; - try { - final String factoryModelFilePath = getFactoryModelFilePathsLocked().get(locale); - if (factoryModelFilePath != null) { - factoryFd = ParcelFileDescriptor.open( - new File(factoryModelFilePath), ParcelFileDescriptor.MODE_READ_ONLY); - if (factoryFd != null) { - factoryVersion = SmartSelection.getVersion(factoryFd.getFd()); - } - } else { - factoryFd = null; - } - } catch (FileNotFoundException e) { - factoryFd = null; - } - - if (updateFd == null) { - if (factoryFd != null) { - return factoryFd; - } else { - throw new FileNotFoundException( - String.format(Locale.US, "No model file found for %s", locale)); - } - } - - final int updateFdInt = updateFd.getFd(); - final boolean localeMatches = Objects.equals( - locale.getLanguage().trim().toLowerCase(), - SmartSelection.getLanguage(updateFdInt).trim().toLowerCase()); - if (factoryFd == null) { - if (localeMatches) { - return updateFd; - } else { - closeAndLogError(updateFd); - throw new FileNotFoundException( - String.format(Locale.US, "No model file found for %s", locale)); - } - } - - if (!localeMatches) { - closeAndLogError(updateFd); - return factoryFd; - } - - if (updateVersion > factoryVersion) { - closeAndLogError(factoryFd); - return updateFd; - } else { - closeAndLogError(updateFd); - return factoryFd; + return DefaultLogger.createSignature(text, start, end, mContext, mModel.getVersion(), + mModel.getSupportedLocales()); } } @@ -377,60 +321,66 @@ public final class TextClassifierImpl implements TextClassifier { } } + /** + * Finds the most appropriate model to use for the given target locale list. + * + * The basic logic is: we ignore all models that don't support any of the target locales. For + * the remaining candidates, we take the update model unless its version number is lower than + * the factory version. It's assumed that factory models do not have overlapping locale ranges + * and conflict resolution between these models hence doesn't matter. + */ @GuardedBy("mLock") // Do not call outside this lock. @Nullable - private Locale findBestSupportedLocaleLocked(LocaleList localeList) { + private ModelFile findBestModelLocked(LocaleList localeList) { // Specified localeList takes priority over the system default, so it is listed first. final String languages = localeList.isEmpty() ? LocaleList.getDefault().toLanguageTags() : localeList.toLanguageTags() + "," + LocaleList.getDefault().toLanguageTags(); final List<Locale.LanguageRange> languageRangeList = Locale.LanguageRange.parse(languages); - final List<Locale> supportedLocales = - new ArrayList<>(getFactoryModelFilePathsLocked().keySet()); - final Locale updatedModelLocale = getUpdatedModelLocale(); - if (updatedModelLocale != null) { - supportedLocales.add(updatedModelLocale); + ModelFile bestModel = null; + int bestModelVersion = -1; + for (ModelFile model : listAllModelsLocked()) { + if (model.isAnyLanguageSupported(languageRangeList)) { + if (model.getVersion() >= bestModelVersion) { + bestModel = model; + bestModelVersion = model.getVersion(); + } + } } - return Locale.lookup(languageRangeList, supportedLocales); + return bestModel; } + /** Returns a list of all model files available, in order of precedence. */ @GuardedBy("mLock") // Do not call outside this lock. - private Map<Locale, String> getFactoryModelFilePathsLocked() { - if (mModelFilePaths == null) { - final Map<Locale, String> modelFilePaths = new HashMap<>(); + private List<ModelFile> listAllModelsLocked() { + if (mAllModelFiles == null) { + final List<ModelFile> allModels = new ArrayList<>(); + // The update model has the highest precedence. + if (new File(UPDATED_MODEL_FILE_PATH).exists()) { + final ModelFile updatedModel = ModelFile.fromPath(UPDATED_MODEL_FILE_PATH); + if (updatedModel != null) { + allModels.add(updatedModel); + } + } + // Factory models should never have overlapping locales, so the order doesn't matter. final File modelsDir = new File(MODEL_DIR); if (modelsDir.exists() && modelsDir.isDirectory()) { - final File[] models = modelsDir.listFiles(); + final File[] modelFiles = modelsDir.listFiles(); final Pattern modelFilenamePattern = Pattern.compile(MODEL_FILE_REGEX); - final int size = models.length; - for (int i = 0; i < size; i++) { - final File modelFile = models[i]; + for (File modelFile : modelFiles) { final Matcher matcher = modelFilenamePattern.matcher(modelFile.getName()); if (matcher.matches() && modelFile.isFile()) { - final String language = matcher.group(1); - final Locale locale = Locale.forLanguageTag(language); - modelFilePaths.put(locale, modelFile.getAbsolutePath()); + final ModelFile model = ModelFile.fromPath(modelFile.getAbsolutePath()); + if (model != null) { + allModels.add(model); + } } } } - mModelFilePaths = modelFilePaths; - } - return mModelFilePaths; - } - - @Nullable - private Locale getUpdatedModelLocale() { - try { - final ParcelFileDescriptor updateFd = ParcelFileDescriptor.open( - new File(UPDATED_MODEL_FILE_PATH), ParcelFileDescriptor.MODE_READ_ONLY); - final Locale locale = Locale.forLanguageTag( - SmartSelection.getLanguage(updateFd.getFd())); - closeAndLogError(updateFd); - return locale; - } catch (FileNotFoundException e) { - return null; + mAllModelFiles = allModels; } + return mAllModelFiles; } private TextClassification createClassificationResult( @@ -520,6 +470,95 @@ public final class TextClassifierImpl implements TextClassifier { } /** + * Describes TextClassifier model files on disk. + */ + private static final class ModelFile { + + private final String mPath; + private final String mName; + private final int mVersion; + private final List<Locale> mSupportedLocales; + + /** Returns null if the path did not point to a compatible model. */ + static @Nullable ModelFile fromPath(String path) { + final File file = new File(path); + try { + final ParcelFileDescriptor modelFd = ParcelFileDescriptor.open( + file, ParcelFileDescriptor.MODE_READ_ONLY); + final int version = SmartSelection.getVersion(modelFd.getFd()); + final String supportedLocalesStr = SmartSelection.getLanguages(modelFd.getFd()); + if (supportedLocalesStr.isEmpty()) { + Log.d(DEFAULT_LOG_TAG, "Ignoring " + file.getAbsolutePath()); + return null; + } + final List<Locale> supportedLocales = new ArrayList<>(); + for (String langTag : supportedLocalesStr.split(",")) { + supportedLocales.add(Locale.forLanguageTag(langTag)); + } + closeAndLogError(modelFd); + return new ModelFile(path, file.getName(), version, supportedLocales); + } catch (FileNotFoundException e) { + Log.e(DEFAULT_LOG_TAG, "Failed to peek " + file.getAbsolutePath(), e); + return null; + } + } + + /** The absolute path to the model file. */ + String getPath() { + return mPath; + } + + /** A name to use for signature generation. Effectively the name of the model file. */ + String getName() { + return mName; + } + + /** Returns the version tag in the model's metadata. */ + int getVersion() { + return mVersion; + } + + /** Returns whether the language supports any language in the given ranges. */ + boolean isAnyLanguageSupported(List<Locale.LanguageRange> languageRanges) { + return Locale.lookup(languageRanges, mSupportedLocales) != null; + } + + /** All locales supported by the model. */ + List<Locale> getSupportedLocales() { + return Collections.unmodifiableList(mSupportedLocales); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } else if (other == null || !ModelFile.class.isAssignableFrom(other.getClass())) { + return false; + } else { + final ModelFile otherModel = (ModelFile) other; + return mPath.equals(otherModel.mPath); + } + } + + @Override + public String toString() { + final StringJoiner localesJoiner = new StringJoiner(","); + for (Locale locale : mSupportedLocales) { + localesJoiner.add(locale.toLanguageTag()); + } + return String.format(Locale.US, "ModelFile { path=%s name=%s version=%d locales=%s }", + mPath, mName, mVersion, localesJoiner.toString()); + } + + private ModelFile(String path, String name, int version, List<Locale> supportedLocales) { + mPath = path; + mName = name; + mVersion = version; + mSupportedLocales = supportedLocales; + } + } + + /** * Creates intents based on the classification type. */ static final class IntentFactory { @@ -541,7 +580,7 @@ public final class TextClassifierImpl implements TextClassifier { case TextClassifier.TYPE_EMAIL: return createForEmail(text); case TextClassifier.TYPE_PHONE: - return createForPhone(text); + return createForPhone(context, text); case TextClassifier.TYPE_ADDRESS: return createForAddress(text); case TextClassifier.TYPE_URL: @@ -573,15 +612,23 @@ public final class TextClassifierImpl implements TextClassifier { } @NonNull - private static List<Intent> createForPhone(String text) { - return Arrays.asList( - new Intent(Intent.ACTION_DIAL) - .setData(Uri.parse(String.format("tel:%s", text))), - new Intent(Intent.ACTION_INSERT_OR_EDIT) - .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE) - .putExtra(ContactsContract.Intents.Insert.PHONE, text), - new Intent(Intent.ACTION_SENDTO) - .setData(Uri.parse(String.format("smsto:%s", text)))); + private static List<Intent> createForPhone(Context context, String text) { + final List<Intent> intents = new ArrayList<>(); + final UserManager userManager = context.getSystemService(UserManager.class); + final Bundle userRestrictions = userManager != null + ? userManager.getUserRestrictions() : new Bundle(); + if (!userRestrictions.getBoolean(UserManager.DISALLOW_OUTGOING_CALLS, false)) { + intents.add(new Intent(Intent.ACTION_DIAL) + .setData(Uri.parse(String.format("tel:%s", text)))); + } + intents.add(new Intent(Intent.ACTION_INSERT_OR_EDIT) + .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE) + .putExtra(ContactsContract.Intents.Insert.PHONE, text)); + if (!userRestrictions.getBoolean(UserManager.DISALLOW_SMS, false)) { + intents.add(new Intent(Intent.ACTION_SENDTO) + .setData(Uri.parse(String.format("smsto:%s", text)))); + } + return intents; } @NonNull diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java index d866d1305172..3d252f293663 100644 --- a/core/java/android/view/textclassifier/TextLinks.java +++ b/core/java/android/view/textclassifier/TextLinks.java @@ -108,6 +108,7 @@ public final class TextLinks implements Parcelable { * @param text the text to apply the links to. Must match the original text * @param applyStrategy strategy for resolving link conflicts * @param spanFactory a factory to generate spans from TextLinks. Will use a default if null + * @param allowPrefix whether to allow applying links only to a prefix of the text. * * @return a status code indicating whether or not the links were successfully applied * @@ -117,10 +118,12 @@ public final class TextLinks implements Parcelable { public int apply( @NonNull Spannable text, @ApplyStrategy int applyStrategy, - @Nullable Function<TextLink, TextLinkSpan> spanFactory) { + @Nullable Function<TextLink, TextLinkSpan> spanFactory, + boolean allowPrefix) { Preconditions.checkNotNull(text); checkValidApplyStrategy(applyStrategy); - if (!mFullText.equals(text.toString())) { + final String textString = text.toString(); + if (!mFullText.equals(textString) && !(allowPrefix && textString.startsWith(mFullText))) { return STATUS_DIFFERENT_TEXT; } if (mLinks.isEmpty()) { diff --git a/core/java/android/view/textclassifier/logging/DefaultLogger.java b/core/java/android/view/textclassifier/logging/DefaultLogger.java index 6b848351cbf6..f510879cf401 100644 --- a/core/java/android/view/textclassifier/logging/DefaultLogger.java +++ b/core/java/android/view/textclassifier/logging/DefaultLogger.java @@ -17,7 +17,6 @@ package android.view.textclassifier.logging; import android.annotation.NonNull; -import android.annotation.Nullable; import android.content.Context; import android.metrics.LogMaker; import android.util.Log; @@ -27,8 +26,10 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.Preconditions; +import java.util.List; import java.util.Locale; import java.util.Objects; +import java.util.StringJoiner; /** * Default Logger. @@ -79,7 +80,7 @@ public final class DefaultLogger extends Logger { Preconditions.checkNotNull(event); final LogMaker log = new LogMaker(MetricsEvent.TEXT_SELECTION_SESSION) .setType(getLogType(event)) - .setSubtype(MetricsEvent.TEXT_SELECTION_INVOCATION_MANUAL) + .setSubtype(getLogSubType(event)) .setPackageName(event.getPackageName()) .addTaggedData(START_EVENT_DELTA, event.getDurationSinceSessionStart()) .addTaggedData(PREV_EVENT_DELTA, event.getDurationSincePreviousEvent()) @@ -136,6 +137,17 @@ public final class DefaultLogger extends Logger { } } + private static int getLogSubType(SelectionEvent event) { + switch (event.getInvocationMethod()) { + case SelectionEvent.INVOCATION_MANUAL: + return MetricsEvent.TEXT_SELECTION_INVOCATION_MANUAL; + case SelectionEvent.INVOCATION_LINK: + return MetricsEvent.TEXT_SELECTION_INVOCATION_LINK; + default: + return MetricsEvent.TEXT_SELECTION_INVOCATION_UNKNOWN; + } + } + private static String getLogTypeString(int logType) { switch (logType) { case MetricsEvent.ACTION_TEXT_SELECTION_OVERTYPE: @@ -175,6 +187,17 @@ public final class DefaultLogger extends Logger { } } + private static String getLogSubTypeString(int logSubType) { + switch (logSubType) { + case MetricsEvent.TEXT_SELECTION_INVOCATION_MANUAL: + return "MANUAL"; + case MetricsEvent.TEXT_SELECTION_INVOCATION_LINK: + return "LINK"; + default: + return UNKNOWN; + } + } + private static void debugLog(LogMaker log) { if (!DEBUG_LOG_ENABLED) return; @@ -192,6 +215,7 @@ public final class DefaultLogger extends Logger { final String model = Objects.toString(log.getTaggedData(MODEL_NAME), UNKNOWN); final String entity = Objects.toString(log.getTaggedData(ENTITY_TYPE), UNKNOWN); final String type = getLogTypeString(log.getType()); + final String subType = getLogSubTypeString(log.getSubtype()); final int smartStart = Integer.parseInt( Objects.toString(log.getTaggedData(SMART_START), ZERO)); final int smartEnd = Integer.parseInt( @@ -201,8 +225,9 @@ public final class DefaultLogger extends Logger { final int eventEnd = Integer.parseInt( Objects.toString(log.getTaggedData(EVENT_END), ZERO)); - Log.d(LOG_TAG, String.format("%2d: %s/%s, range=%d,%d - smart_range=%d,%d (%s/%s)", - index, type, entity, eventStart, eventEnd, smartStart, smartEnd, widget, model)); + Log.d(LOG_TAG, String.format("%2d: %s/%s/%s, range=%d,%d - smart_range=%d,%d (%s/%s)", + index, type, subType, entity, eventStart, eventEnd, smartStart, smartEnd, widget, + model)); } /** @@ -210,12 +235,16 @@ public final class DefaultLogger extends Logger { */ public static String createSignature( String text, int start, int end, Context context, int modelVersion, - @Nullable Locale locale) { + List<Locale> locales) { Preconditions.checkNotNull(text); Preconditions.checkNotNull(context); - final String modelName = (locale != null) - ? String.format(Locale.US, "%s_v%d", locale.toLanguageTag(), modelVersion) - : ""; + Preconditions.checkNotNull(locales); + final StringJoiner localesJoiner = new StringJoiner(","); + for (Locale locale : locales) { + localesJoiner.add(locale.toLanguageTag()); + } + final String modelName = String.format(Locale.US, "%s_v%d", localesJoiner.toString(), + modelVersion); final int hash = Objects.hash(text, start, end, context.getPackageName()); return SignatureParser.createSignature(CLASSIFIER_ID, modelName, hash); } @@ -242,9 +271,9 @@ public final class DefaultLogger extends Logger { static String getModelName(String signature) { Preconditions.checkNotNull(signature); - final int start = signature.indexOf("|"); + final int start = signature.indexOf("|") + 1; final int end = signature.indexOf("|", start); - if (start >= 0 && end >= start) { + if (start >= 1 && end >= start) { return signature.substring(start, end); } return ""; diff --git a/core/java/android/view/textclassifier/logging/Logger.java b/core/java/android/view/textclassifier/logging/Logger.java index 40e4d8ce1a77..4448b2b5b494 100644 --- a/core/java/android/view/textclassifier/logging/Logger.java +++ b/core/java/android/view/textclassifier/logging/Logger.java @@ -71,6 +71,7 @@ public abstract class Logger { public static final String WIDGET_CUSTOM_UNSELECTABLE_TEXTVIEW = "nosel-customview"; public static final String WIDGET_UNKNOWN = "unknown"; + private @SelectionEvent.InvocationMethod int mInvocationMethod; private SelectionEvent mPrevEvent; private SelectionEvent mSmartEvent; private SelectionEvent mStartEvent; @@ -124,16 +125,19 @@ public abstract class Logger { /** * Logs a "selection started" event. * + * @param invocationMethod the way the selection was triggered * @param start the token index of the selected token */ - public final void logSelectionStartedEvent(int start) { + public final void logSelectionStartedEvent( + @SelectionEvent.InvocationMethod int invocationMethod, int start) { if (mConfig == null) { return; } + mInvocationMethod = invocationMethod; logEvent(new SelectionEvent( start, start + 1, SelectionEvent.EVENT_SELECTION_STARTED, - TextClassifier.TYPE_UNKNOWN, NO_SIGNATURE, mConfig)); + TextClassifier.TYPE_UNKNOWN, mInvocationMethod, NO_SIGNATURE, mConfig)); } /** @@ -152,7 +156,7 @@ public abstract class Logger { logEvent(new SelectionEvent( start, end, SelectionEvent.EVENT_SELECTION_MODIFIED, - TextClassifier.TYPE_UNKNOWN, NO_SIGNATURE, mConfig)); + TextClassifier.TYPE_UNKNOWN, mInvocationMethod, NO_SIGNATURE, mConfig)); } /** @@ -179,7 +183,7 @@ public abstract class Logger { final String signature = classification.getSignature(); logEvent(new SelectionEvent( start, end, SelectionEvent.EVENT_SELECTION_MODIFIED, - entityType, signature, mConfig)); + entityType, mInvocationMethod, signature, mConfig)); } /** @@ -213,7 +217,8 @@ public abstract class Logger { ? selection.getEntity(0) : TextClassifier.TYPE_UNKNOWN; final String signature = selection.getSignature(); - logEvent(new SelectionEvent(start, end, eventType, entityType, signature, mConfig)); + logEvent(new SelectionEvent(start, end, eventType, entityType, mInvocationMethod, signature, + mConfig)); } /** @@ -234,7 +239,8 @@ public abstract class Logger { } logEvent(new SelectionEvent( - start, end, actionType, TextClassifier.TYPE_UNKNOWN, NO_SIGNATURE, mConfig)); + start, end, actionType, TextClassifier.TYPE_UNKNOWN, mInvocationMethod, + NO_SIGNATURE, mConfig)); } /** @@ -265,7 +271,8 @@ public abstract class Logger { ? classification.getEntity(0) : TextClassifier.TYPE_UNKNOWN; final String signature = classification.getSignature(); - logEvent(new SelectionEvent(start, end, actionType, entityType, signature, mConfig)); + logEvent(new SelectionEvent(start, end, actionType, entityType, mInvocationMethod, + signature, mConfig)); } private void logEvent(@NonNull SelectionEvent event) { diff --git a/core/java/android/view/textclassifier/logging/SelectionEvent.java b/core/java/android/view/textclassifier/logging/SelectionEvent.java index f40b65571142..a8de3088d8cc 100644 --- a/core/java/android/view/textclassifier/logging/SelectionEvent.java +++ b/core/java/android/view/textclassifier/logging/SelectionEvent.java @@ -98,6 +98,16 @@ public final class SelectionEvent { /** Something else other than User or the default TextClassifier triggered a selection. */ public static final int EVENT_AUTO_SELECTION = 5; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({INVOCATION_MANUAL, INVOCATION_LINK}) + public @interface InvocationMethod {} + + /** Selection was invoked by the user long pressing, double tapping, or dragging to select. */ + public static final int INVOCATION_MANUAL = 1; + /** Selection was invoked by the user tapping on a link. */ + public static final int INVOCATION_LINK = 2; + private final int mAbsoluteStart; private final int mAbsoluteEnd; private final @EventType int mEventType; @@ -105,6 +115,7 @@ public final class SelectionEvent { @Nullable private final String mWidgetVersion; private final String mPackageName; private final String mWidgetType; + private final @InvocationMethod int mInvocationMethod; // These fields should only be set by creator of a SelectionEvent. private String mSignature; @@ -121,7 +132,7 @@ public final class SelectionEvent { SelectionEvent( int start, int end, @EventType int eventType, @EntityType String entityType, - String signature, Logger.Config config) { + @InvocationMethod int invocationMethod, String signature, Logger.Config config) { Preconditions.checkArgument(end >= start, "end cannot be less than start"); mAbsoluteStart = start; mAbsoluteEnd = end; @@ -132,6 +143,7 @@ public final class SelectionEvent { mWidgetVersion = config.getWidgetVersion(); mPackageName = Preconditions.checkNotNull(config.getPackageName()); mWidgetType = Preconditions.checkNotNull(config.getWidgetType()); + mInvocationMethod = invocationMethod; } int getAbsoluteStart() { @@ -180,6 +192,13 @@ public final class SelectionEvent { } /** + * Returns the way the selection mode was invoked. + */ + public @InvocationMethod int getInvocationMethod() { + return mInvocationMethod; + } + + /** * Returns the signature of the text classifier result associated with this event. */ public String getSignature() { diff --git a/core/java/android/webkit/TracingConfig.java b/core/java/android/webkit/TracingConfig.java index 75e2bf7026c5..68badecaec3a 100644 --- a/core/java/android/webkit/TracingConfig.java +++ b/core/java/android/webkit/TracingConfig.java @@ -21,61 +21,76 @@ import android.annotation.NonNull; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; /** * Holds tracing configuration information and predefined settings. */ public class TracingConfig { - private final String mCustomCategoryPattern; - private final @PresetCategories int mPresetCategories; + private @PredefinedCategories int mPredefinedCategories; + private final List<String> mCustomIncludedCategories = new ArrayList<String>(); private @TracingMode int mTracingMode; /** @hide */ - @IntDef({CATEGORIES_NONE, CATEGORIES_WEB_DEVELOPER, CATEGORIES_INPUT_LATENCY, - CATEGORIES_RENDERING, CATEGORIES_JAVASCRIPT_AND_RENDERING, CATEGORIES_FRAME_VIEWER}) + @IntDef(flag = true, value = {CATEGORIES_NONE, CATEGORIES_WEB_DEVELOPER, + CATEGORIES_INPUT_LATENCY, CATEGORIES_RENDERING, CATEGORIES_JAVASCRIPT_AND_RENDERING, + CATEGORIES_FRAME_VIEWER}) @Retention(RetentionPolicy.SOURCE) - public @interface PresetCategories {} + public @interface PredefinedCategories {} /** - * Indicates that there are no preset categories. + * Indicates that there are no predefined categories. */ - public static final int CATEGORIES_NONE = -1; + public static final int CATEGORIES_NONE = 0; /** - * Predefined categories typically useful for web developers. + * Predefined set of categories, includes all categories enabled by default in chromium. + * Use with caution: this setting may produce large trace output. + */ + public static final int CATEGORIES_ALL = 1 << 0; + + /** + * Predefined set of categories typically useful for analyzing WebViews. + * Typically includes android_webview and Java. + */ + public static final int CATEGORIES_ANDROID_WEBVIEW = 1 << 1; + + /** + * Predefined set of categories typically useful for web developers. * Typically includes blink, compositor, renderer.scheduler and v8 categories. */ - public static final int CATEGORIES_WEB_DEVELOPER = 0; + public static final int CATEGORIES_WEB_DEVELOPER = 1 << 2; /** - * Predefined categories for analyzing input latency issues. + * Predefined set of categories for analyzing input latency issues. * Typically includes input, renderer.scheduler categories. */ - public static final int CATEGORIES_INPUT_LATENCY = 1; + public static final int CATEGORIES_INPUT_LATENCY = 1 << 3; /** - * Predefined categories for analyzing rendering issues. + * Predefined set of categories for analyzing rendering issues. * Typically includes blink, compositor and gpu categories. */ - public static final int CATEGORIES_RENDERING = 2; + public static final int CATEGORIES_RENDERING = 1 << 4; /** - * Predefined categories for analyzing javascript and rendering issues. - * Typically includes blink, compositor, gpu, renderer.schduler and v8 categories. + * Predefined set of categories for analyzing javascript and rendering issues. + * Typically includes blink, compositor, gpu, renderer.scheduler and v8 categories. */ - public static final int CATEGORIES_JAVASCRIPT_AND_RENDERING = 3; + public static final int CATEGORIES_JAVASCRIPT_AND_RENDERING = 1 << 5; /** - * Predefined categories for studying difficult rendering performance problems. + * Predefined set of categories for studying difficult rendering performance problems. * Typically includes blink, compositor, gpu, renderer.scheduler, v8 and * some other compositor categories which are disabled by default. */ - public static final int CATEGORIES_FRAME_VIEWER = 4; + public static final int CATEGORIES_FRAME_VIEWER = 1 << 6; /** @hide */ - @IntDef({RECORD_UNTIL_FULL, RECORD_CONTINUOUSLY, RECORD_UNTIL_FULL_LARGE_BUFFER, - RECORD_TO_CONSOLE}) + @IntDef({RECORD_UNTIL_FULL, RECORD_CONTINUOUSLY, RECORD_UNTIL_FULL_LARGE_BUFFER}) @Retention(RetentionPolicy.SOURCE) public @interface TracingMode {} @@ -97,99 +112,38 @@ public class TracingConfig { /** * Record trace events using a larger internal tracing buffer until it is full. - * Uses more memory than the other modes and may not be suitable on devices - * with smaller RAM. Depending on the implementation typically allows up to - * 512 million events to be stored. + * Uses significantly more memory than {@link #RECORD_UNTIL_FULL} and may not be + * suitable on devices with smaller RAM. */ public static final int RECORD_UNTIL_FULL_LARGE_BUFFER = 2; /** - * Record trace events to console (logcat). The events are discarded and nothing - * is sent back to the caller. Uses the least memory as compared to the other modes. - */ - public static final int RECORD_TO_CONSOLE = 3; - - /** - * Create config with the preset categories. - * <p> - * Example: - * TracingConfig(CATEGORIES_WEB_DEVELOPER) -- records trace events from the "web developer" - * preset categories. - * - * @param presetCategories preset categories to use, one of {@link #CATEGORIES_WEB_DEVELOPER}, - * {@link #CATEGORIES_INPUT_LATENCY}, {@link #CATEGORIES_RENDERING}, - * {@link #CATEGORIES_JAVASCRIPT_AND_RENDERING} or - * {@link #CATEGORIES_FRAME_VIEWER}. - * - * Note: for specifying custom categories without presets use - * {@link #TracingConfig(int, String, int)}. - * + * @hide */ - public TracingConfig(@PresetCategories int presetCategories) { - this(presetCategories, "", RECORD_UNTIL_FULL); + public TracingConfig(@PredefinedCategories int predefinedCategories, + @NonNull List<String> customIncludedCategories, + @TracingMode int tracingMode) { + mPredefinedCategories = predefinedCategories; + mCustomIncludedCategories.addAll(customIncludedCategories); + mTracingMode = tracingMode; } /** - * Create a configuration with both preset categories and custom categories. - * Also allows to specify the tracing mode. - * - * Note that the categories are defined by the currently-in-use version of WebView. They live - * in chromium code and are not part of the Android API. See - * See <a href="https://www.chromium.org/developers/how-tos/trace-event-profiling-tool"> - * chromium documentation on tracing</a> for more details. - * - * <p> - * Examples: - * - * Preset category with a specified trace mode: - * TracingConfig(CATEGORIES_WEB_DEVELOPER, "", RECORD_UNTIL_FULL_LARGE_BUFFER); - * Custom categories: - * TracingConfig(CATEGORIES_NONE, "browser", RECORD_UNTIL_FULL) - * -- records only the trace events from the "browser" category. - * TraceConfig(CATEGORIES_NONE, "-input,-gpu", RECORD_UNTIL_FULL) - * -- records all trace events excluding the events from the "input" and 'gpu' categories. - * TracingConfig(CATEGORIES_NONE, "blink*,devtools*", RECORD_UNTIL_FULL) - * -- records only the trace events matching the "blink*" and "devtools*" patterns - * (e.g. "blink_gc" and "devtools.timeline" categories). - * - * Combination of preset and additional custom categories: - * TracingConfig(CATEGORIES_WEB_DEVELOPER, "memory-infra", RECORD_CONTINUOUSLY) - * -- records events from the "web developer" categories and events from the "memory-infra" - * category to understand where memory is being used. - * - * @param presetCategories preset categories to use, one of {@link #CATEGORIES_WEB_DEVELOPER}, - * {@link #CATEGORIES_INPUT_LATENCY}, {@link #CATEGORIES_RENDERING}, - * {@link #CATEGORIES_JAVASCRIPT_AND_RENDERING} or - * {@link #CATEGORIES_FRAME_VIEWER}. - * @param customCategories a comma-delimited list of category wildcards. A category can - * have an optional '-' prefix to make it an excluded category. - * @param tracingMode tracing mode to use, one of {@link #RECORD_UNTIL_FULL}, - * {@link #RECORD_CONTINUOUSLY}, {@link #RECORD_UNTIL_FULL_LARGE_BUFFER} - * or {@link #RECORD_TO_CONSOLE}. - */ - public TracingConfig(@PresetCategories int presetCategories, - @NonNull String customCategories, @TracingMode int tracingMode) { - mPresetCategories = presetCategories; - mCustomCategoryPattern = customCategories; - mTracingMode = RECORD_UNTIL_FULL; + * Returns a bitmask of the predefined categories values of this configuration. + */ + @PredefinedCategories + public int getPredefinedCategories() { + return mPredefinedCategories; } /** - * Returns the custom category pattern for this configuration. + * Returns the list of included custom category patterns for this configuration. * - * @return empty string if no custom category pattern is specified. + * @return empty list if no custom category patterns are specified. */ @NonNull - public String getCustomCategoryPattern() { - return mCustomCategoryPattern; - } - - /** - * Returns the preset categories value of this configuration. - */ - @PresetCategories - public int getPresetCategories() { - return mPresetCategories; + public List<String> getCustomIncludedCategories() { + return mCustomIncludedCategories; } /** @@ -200,4 +154,111 @@ public class TracingConfig { return mTracingMode; } + /** + * Builder used to create {@link TracingConfig} objects. + * + * Examples: + * new TracingConfig.Builder().build() + * -- creates a configuration with default options: {@link #CATEGORIES_NONE}, + * {@link #RECORD_UNTIL_FULL}. + * new TracingConfig.Builder().addCategories(CATEGORIES_WEB_DEVELOPER).build() + * -- records trace events from the "web developer" predefined category sets. + * new TracingConfig.Builder().addCategories(CATEGORIES_RENDERING, + * CATEGORIES_INPUT_LATENCY).build() + * -- records trace events from the "rendering" and "input latency" predefined + * category sets. + * new TracingConfig.Builder().addCategories("browser").build() + * -- records only the trace events from the "browser" category. + * new TracingConfig.Builder().addCategories("blink*","renderer*").build() + * -- records only the trace events matching the "blink*" and "renderer*" patterns + * (e.g. "blink.animations", "renderer_host" and "renderer.scheduler" categories). + * new TracingConfig.Builder().addCategories(CATEGORIES_WEB_DEVELOPER) + * .addCategories("disabled-by-default-v8.gc") + * .setTracingMode(RECORD_CONTINUOUSLY).build() + * -- records events from the "web developer" predefined category set and events from + * the "disabled-by-default-v8.gc" category to understand where garbage collection + * is being triggered. Uses a ring buffer for internal storage during tracing. + */ + public static class Builder { + private @PredefinedCategories int mPredefinedCategories = CATEGORIES_NONE; + private final List<String> mCustomIncludedCategories = new ArrayList<String>(); + private @TracingMode int mTracingMode = RECORD_UNTIL_FULL; + + /** + * Default constructor for Builder. + */ + public Builder() {} + + /** + * Build {@link TracingConfig} using the current settings. + */ + public TracingConfig build() { + return new TracingConfig(mPredefinedCategories, mCustomIncludedCategories, + mTracingMode); + } + + /** + * Adds categories from a predefined set of categories to be included in the trace output. + * + * @param predefinedCategories list or bitmask of predefined category sets to use: + * {@link #CATEGORIES_NONE}, {@link #CATEGORIES_ALL}, + * {@link #CATEGORIES_WEB_DEVELOPER}, {@link #CATEGORIES_INPUT_LATENCY}, + * {@link #CATEGORIES_RENDERING}, + * {@link #CATEGORIES_JAVASCRIPT_AND_RENDERING} or + * {@link #CATEGORIES_FRAME_VIEWER}. + * @return The builder to facilitate chaining. + */ + public Builder addCategories(@PredefinedCategories int... predefinedCategories) { + for (int categorySet : predefinedCategories) { + mPredefinedCategories |= categorySet; + } + return this; + } + + /** + * Adds custom categories to be included in trace output. + * + * Note that the categories are defined by the currently-in-use version of WebView. They + * live in chromium code and are not part of the Android API. See + * See <a href="https://www.chromium.org/developers/how-tos/trace-event-profiling-tool"> + * chromium documentation on tracing</a> for more details. + * + * @param categories a list of category patterns. A category pattern can contain wilcards, + * e.g. "blink*" or full category name e.g. "renderer.scheduler". + * @return The builder to facilitate chaining. + */ + public Builder addCategories(String... categories) { + for (String category: categories) { + mCustomIncludedCategories.add(category); + } + return this; + } + + /** + * Adds custom categories to be included in trace output. + * + * Same as {@link #addCategories(String...)} but allows to pass a Collection as a parameter. + * + * @param categories a list of category patters. + * @return The builder to facilitate chaining. + */ + public Builder addCategories(Collection<String> categories) { + mCustomIncludedCategories.addAll(categories); + return this; + } + + /** + * Sets the tracing mode for this configuration. + * + * @param tracingMode tracing mode to use, one of {@link #RECORD_UNTIL_FULL}, + * {@link #RECORD_CONTINUOUSLY} or + * {@link #RECORD_UNTIL_FULL_LARGE_BUFFER}. + * @return The builder to facilitate chaining. + */ + public Builder setTracingMode(@TracingMode int tracingMode) { + mTracingMode = tracingMode; + return this; + } + } + } diff --git a/core/java/android/webkit/TracingController.java b/core/java/android/webkit/TracingController.java index cadb8a184072..7871021a33c8 100644 --- a/core/java/android/webkit/TracingController.java +++ b/core/java/android/webkit/TracingController.java @@ -16,9 +16,12 @@ package android.webkit; +import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; -import android.os.Handler; + +import java.io.OutputStream; +import java.util.concurrent.Executor; /** * Manages tracing of WebViews. In particular provides functionality for the app @@ -29,40 +32,22 @@ import android.os.Handler; * The resulting trace data is sent back as a byte sequence in json format. This * file can be loaded in "chrome://tracing" for further analysis. * <p> - * Note: All methods in this class must be called on the UI thread. All callbacks - * are also called on the UI thread. - * <p> * Example usage: * <pre class="prettyprint"> * TracingController tracingController = TracingController.getInstance(); - * tracingController.start(new TraceConfig(CATEGORIES_WEB_DEVELOPER)); + * tracingController.start(new TraceConfig.Builder() + * .addCategories(CATEGORIES_WEB_DEVELOPER).build()); * [..] - * tracingController.stopAndFlush(new TraceFileOutput("trace.json"), null); + * tracingController.stop(new FileOutputStream("trace.json"), + * Executors.newSingleThreadExecutor()); * </pre></p> */ public abstract class TracingController { /** - * Interface for capturing tracing data. - */ - public interface TracingOutputStream { - /** - * Will be called to return tracing data in chunks. - * Tracing data is returned in json format an array of bytes. - */ - void write(byte[] chunk); - - /** - * Called when tracing is finished and the data collection is over. - * There will be no calls to #write after #complete is called. - */ - void complete(); - } - - /** * Returns the default TracingController instance. At present there is * only one TracingController instance for all WebView instances, - * however this restriction may be relaxed in the future. + * however this restriction may be relaxed in a future Android release. * * @return the default TracingController instance */ @@ -72,55 +57,38 @@ public abstract class TracingController { } /** - * Starts tracing all webviews. Depeding on the trace mode in traceConfig + * Starts tracing all webviews. Depending on the trace mode in traceConfig * specifies how the trace events are recorded. * * For tracing modes {@link TracingConfig#RECORD_UNTIL_FULL}, * {@link TracingConfig#RECORD_CONTINUOUSLY} and * {@link TracingConfig#RECORD_UNTIL_FULL_LARGE_BUFFER} the events are recorded * using an internal buffer and flushed to the outputStream when - * {@link #stopAndFlush(TracingOutputStream, Handler)} is called. + * {@link #stop(OutputStream, Executor)} is called. * * @param tracingConfig configuration options to use for tracing - * @return false if the system is already tracing, true otherwise. + * @throws IllegalStateException if the system is already tracing. */ - public abstract boolean start(TracingConfig tracingConfig); + public abstract void start(@NonNull TracingConfig tracingConfig); /** - * Stops tracing and discards all tracing data. + * Stops tracing and flushes tracing data to the specified outputStream. * - * This method is particularly useful in conjunction with the - * {@link TracingConfig#RECORD_TO_CONSOLE} tracing mode because tracing data is logged to - * console and not sent to an outputStream as with - * {@link #stopAndFlush(TracingOutputStream, Handler)}. + * The data is sent to the specified output stream in json format typically + * in chunks by invoking {@link java.io.OutputStream#write(byte[])}. On completion + * the {@link java.io.OutputStream#close()} method is called. * + * @param outputStream the output steam the tracing data will be sent to. If null + * the tracing data will be discarded. + * @param executor the {@link java.util.concurrent.Executor} on which the + * outputStream #write and #close methods will be invoked. * @return false if the system was not tracing at the time of the call, true * otherwise. */ - public abstract boolean stop(); - - /** - * Stops tracing and flushes tracing data to the specifid outputStream. - * - * Note that if the {@link TracingConfig#RECORD_TO_CONSOLE} tracing mode is used - * nothing will be sent to the outputStream and no TracingOuputStream methods will be - * called. In that case it is more convenient to just use {@link #stop()} instead. - * - * @param outputStream the output steam the tracing data will be sent to. - * @param handler the {@link android.os.Handler} on which the outputStream callbacks - * will be invoked. If the handler is null the current thread's Looper - * will be used. - * @return false if the system was not tracing at the time of the call, true - * otherwise. - */ - public abstract boolean stopAndFlush(TracingOutputStream outputStream, - @Nullable Handler handler); + public abstract boolean stop(@Nullable OutputStream outputStream, + @NonNull @CallbackExecutor Executor executor); /** True if the system is tracing */ public abstract boolean isTracing(); - // TODO: consider adding getTraceBufferUsage, percentage and approx event count. - // TODO: consider adding String getCategories(), for obtaining the actual list - // of categories used (given that presets are ints). - } diff --git a/core/java/android/webkit/TracingFileOutputStream.java b/core/java/android/webkit/TracingFileOutputStream.java deleted file mode 100644 index 8a5fa36c2d99..000000000000 --- a/core/java/android/webkit/TracingFileOutputStream.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 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.webkit; - -import android.annotation.NonNull; - -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; - -/** - * Simple TracingOutputStream implementation which writes the trace data from - * {@link TracingController} to a new file. - * - */ -public class TracingFileOutputStream implements TracingController.TracingOutputStream { - - private FileOutputStream mFileOutput; - - public TracingFileOutputStream(@NonNull String filename) throws FileNotFoundException { - mFileOutput = new FileOutputStream(filename); - } - - /** - * Writes bytes chunk to the file. - */ - public void write(byte[] chunk) { - try { - mFileOutput.write(chunk); - } catch (IOException e) { - onIOException(e); - } - } - - /** - * Closes the file. - */ - public void complete() { - try { - mFileOutput.close(); - } catch (IOException e) { - onIOException(e); - } - } - - private void onIOException(IOException e) { - throw new RuntimeException(e); - } -} diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 5ab579df5ed6..998866137dcf 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -1299,6 +1299,16 @@ public class Editor { if (mSelectionModifierCursorController != null) { mSelectionModifierCursorController.resetTouchOffsets(); } + + ensureNoSelectionIfNonSelectable(); + } + } + + private void ensureNoSelectionIfNonSelectable() { + // This could be the case if a TextLink has been tapped. + if (!mTextView.textCanBeSelected() && mTextView.hasSelection()) { + Selection.setSelection((Spannable) mTextView.getText(), + mTextView.length(), mTextView.length()); } } @@ -1382,6 +1392,8 @@ public class Editor { // Don't leave us in the middle of a batch edit. Same as in onFocusChanged ensureEndedBatchEdit(); + + ensureNoSelectionIfNonSelectable(); } } @@ -4174,7 +4186,7 @@ public class Editor { primaryHorizontal, layout.getLineTop(line), primaryHorizontal, - layout.getLineBottom(line) - layout.getLineBottom(line) + mHandleHeight); + layout.getLineBottom(line) + mHandleHeight); } // Take TextView's padding and scroll into account. int textHorizontalOffset = mTextView.viewportToContentHorizontalOffset(); diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java index 012b918ff34e..3aae8497ba1b 100644 --- a/core/java/android/widget/GridLayout.java +++ b/core/java/android/widget/GridLayout.java @@ -904,6 +904,9 @@ public class GridLayout extends ViewGroup { } } + /** + * @hide + */ @Override protected void onDebugDrawMargins(Canvas canvas, Paint paint) { // Apply defaults, so as to remove UNDEFINED values @@ -919,6 +922,9 @@ public class GridLayout extends ViewGroup { } } + /** + * @hide + */ @Override protected void onDebugDraw(Canvas canvas) { Paint paint = new Paint(); diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index 7ea1f1edadf5..d32e93c7a862 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -917,7 +917,7 @@ public class LinearLayout extends ViewGroup { // measurement on any children, we need to measure them now. int remainingExcess = heightSize - mTotalLength + (mAllowInconsistentMeasurement ? 0 : consumedExcessSpace); - if (skippedMeasure || remainingExcess != 0 && totalWeight > 0.0f) { + if (skippedMeasure || totalWeight > 0.0f) { float remainingWeightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight; mTotalLength = 0; @@ -1300,7 +1300,7 @@ public class LinearLayout extends ViewGroup { // measurement on any children, we need to measure them now. int remainingExcess = widthSize - mTotalLength + (mAllowInconsistentMeasurement ? 0 : usedExcessSpace); - if (skippedMeasure || remainingExcess != 0 && totalWeight > 0.0f) { + if (skippedMeasure || totalWeight > 0.0f) { float remainingWeightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight; maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1; diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java index 7a4c800ba15e..88365617cd6e 100644 --- a/core/java/android/widget/Magnifier.java +++ b/core/java/android/widget/Magnifier.java @@ -18,8 +18,10 @@ package android.widget; import android.annotation.FloatRange; import android.annotation.NonNull; +import android.annotation.TestApi; import android.annotation.UiThread; import android.content.Context; +import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Point; import android.graphics.PointF; @@ -269,4 +271,46 @@ public final class Magnifier { return mWindow.getContentView().findViewById( com.android.internal.R.id.magnifier_image); } + + /** + * @return the content being currently displayed in the magnifier, as bitmap + * + * @hide + */ + @TestApi + public Bitmap getContent() { + return mBitmap; + } + + /** + * @return the position of the magnifier window relative to the screen + * + * @hide + */ + @TestApi + public Rect getWindowPositionOnScreen() { + final int[] viewLocationOnScreen = new int[2]; + mView.getLocationOnScreen(viewLocationOnScreen); + final int[] viewLocationInSurface = new int[2]; + mView.getLocationInSurface(viewLocationInSurface); + + final int left = mWindowCoords.x + viewLocationOnScreen[0] - viewLocationInSurface[0]; + final int top = mWindowCoords.y + viewLocationOnScreen[1] - viewLocationInSurface[1]; + return new Rect(left, top, left + mWindow.getWidth(), top + mWindow.getHeight()); + } + + /** + * @return the size of the magnifier window in dp + * + * @hide + */ + @TestApi + public static PointF getMagnifierDefaultSize() { + final Resources resources = Resources.getSystem(); + final float density = resources.getDisplayMetrics().density; + final PointF size = new PointF(); + size.x = resources.getDimension(com.android.internal.R.dimen.magnifier_width) / density; + size.y = resources.getDimension(com.android.internal.R.dimen.magnifier_height) / density; + return size; + } } diff --git a/core/java/android/widget/OWNERS b/core/java/android/widget/OWNERS index 8f0d02f5d788..e4b2930a23cb 100644 --- a/core/java/android/widget/OWNERS +++ b/core/java/android/widget/OWNERS @@ -1,12 +1,11 @@ per-file TextView.java = siyamed@google.com per-file TextView.java = nona@google.com per-file TextView.java = clarabayarri@google.com -per-file TextView.java = toki@google.com + per-file EditText.java = siyamed@google.com per-file EditText.java = nona@google.com per-file EditText.java = clarabayarri@google.com -per-file EditText.java = toki@google.com + per-file Editor.java = siyamed@google.com per-file Editor.java = nona@google.com per-file Editor.java = clarabayarri@google.com -per-file Editor.java = toki@google.com diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index e91db1390582..7217def3cf08 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -1583,7 +1583,7 @@ public class PopupWindow { * * @hide */ - protected final boolean findDropDownPosition(View anchor, WindowManager.LayoutParams outParams, + protected boolean findDropDownPosition(View anchor, WindowManager.LayoutParams outParams, int xOffset, int yOffset, int width, int height, int gravity, boolean allowScroll) { final int anchorHeight = anchor.getHeight(); final int anchorWidth = anchor.getWidth(); diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java index 5c4d4d2a7fa0..c98714799742 100644 --- a/core/java/android/widget/RadioGroup.java +++ b/core/java/android/widget/RadioGroup.java @@ -183,13 +183,17 @@ public class RadioGroup extends LinearLayout { } private void setCheckedId(@IdRes int id) { + boolean changed = id != mCheckedId; mCheckedId = id; + if (mOnCheckedChangeListener != null) { mOnCheckedChangeListener.onCheckedChanged(this, mCheckedId); } - final AutofillManager afm = mContext.getSystemService(AutofillManager.class); - if (afm != null) { - afm.notifyValueChanged(this); + if (changed) { + final AutofillManager afm = mContext.getSystemService(AutofillManager.class); + if (afm != null) { + afm.notifyValueChanged(this); + } } } diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java index e7a4c0246e1f..6ab09d6cbe74 100644 --- a/core/java/android/widget/SelectionActionModeHelper.java +++ b/core/java/android/widget/SelectionActionModeHelper.java @@ -111,7 +111,8 @@ public final class SelectionActionModeHelper { mSelectionTracker.onOriginalSelection( getText(mTextView), mTextView.getSelectionStart(), - mTextView.getSelectionEnd()); + mTextView.getSelectionEnd(), + false /*isLink*/); cancelAsyncTask(); if (skipTextClassification()) { startSelectionActionMode(null); @@ -134,7 +135,11 @@ public final class SelectionActionModeHelper { * Starts Link ActionMode. */ public void startLinkActionModeAsync(TextLinks.TextLink textLink) { - //TODO: tracking/logging + mSelectionTracker.onOriginalSelection( + getText(mTextView), + mTextView.getSelectionStart(), + mTextView.getSelectionEnd(), + true /*isLink*/); cancelAsyncTask(); if (skipTextClassification()) { startLinkActionMode(null); @@ -487,7 +492,8 @@ public final class SelectionActionModeHelper { /** * Called when the original selection happens, before smart selection is triggered. */ - public void onOriginalSelection(CharSequence text, int selectionStart, int selectionEnd) { + public void onOriginalSelection( + CharSequence text, int selectionStart, int selectionEnd, boolean isLink) { // If we abandoned a selection and created a new one very shortly after, we may still // have a pending request to log ABANDON, which we flush here. mDelayedLogAbandon.flush(); @@ -496,7 +502,8 @@ public final class SelectionActionModeHelper { mOriginalEnd = mSelectionEnd = selectionEnd; mAllowReset = false; maybeInvalidateLogger(); - mLogger.logSelectionStarted(text, selectionStart); + mLogger.logSelectionStarted(text, selectionStart, + isLink ? SelectionEvent.INVOCATION_LINK : SelectionEvent.INVOCATION_MANUAL); } /** @@ -679,7 +686,9 @@ public final class SelectionActionModeHelper { return Logger.WIDGET_UNSELECTABLE_TEXTVIEW; } - public void logSelectionStarted(CharSequence text, int index) { + public void logSelectionStarted( + CharSequence text, int index, + @SelectionEvent.InvocationMethod int invocationMethod) { try { Preconditions.checkNotNull(text); Preconditions.checkArgumentInRange(index, 0, text.length(), "index"); @@ -688,7 +697,7 @@ public final class SelectionActionModeHelper { } mTokenIterator.setText(mText); mStartIndex = index; - mLogger.logSelectionStartedEvent(0); + mLogger.logSelectionStartedEvent(invocationMethod, 0); } catch (Exception e) { // Avoid crashes due to logging. Log.d(LOG_TAG, e.getMessage()); diff --git a/core/java/android/widget/TextInputTimePickerView.java b/core/java/android/widget/TextInputTimePickerView.java index 0cf8faad1c57..e0261ad04f58 100644 --- a/core/java/android/widget/TextInputTimePickerView.java +++ b/core/java/android/widget/TextInputTimePickerView.java @@ -174,7 +174,8 @@ public class TextInputTimePickerView extends RelativeLayout { */ void updateTextInputValues(int localizedHour, int minute, int amOrPm, boolean is24Hour, boolean hourFormatStartsAtZero) { - final String format = "%d"; + final String hourFormat = "%d"; + final String minuteFormat = "%02d"; mIs24Hour = is24Hour; mHourFormatStartsAtZero = hourFormatStartsAtZero; @@ -187,8 +188,8 @@ public class TextInputTimePickerView extends RelativeLayout { mAmPmSpinner.setSelection(1); } - mHourEditText.setText(String.format(format, localizedHour)); - mMinuteEditText.setText(String.format(format, minute)); + mHourEditText.setText(String.format(hourFormat, localizedHour)); + mMinuteEditText.setText(String.format(minuteFormat, minute)); if (mErrorShowing) { validateInput(); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 5710db3ce8e0..1e02c3062d97 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -791,11 +791,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // mAutoSizeStepGranularityInPx. private boolean mHasPresetAutoSizeValues = false; + // Autofill-related attributes + // // Indicates whether the text was set statically or dynamically, so it can be used to // sanitize autofill requests. private boolean mTextSetFromXmlOrResourceId = false; - // Resource id used to set the text - used for autofill purposes. + // Resource id used to set the text. private @StringRes int mTextId = ResourceId.ID_NULL; + // Last value used on AFM.notifyValueChanged(), used to optimize autofill workflow by avoiding + // calls when the value did not change + private CharSequence mLastValueSentToAutofillManager; + // + // End of autofill-related attributes /** * Kick-start the font cache for the zygote process (to pay the cost of @@ -5665,7 +5672,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (needEditableForNotification) { sendAfterTextChanged((Editable) text); } else { - // Always notify AutoFillManager - it will return right away if autofill is disabled. notifyAutoFillManagerAfterTextChangedIfNeeded(); } @@ -9697,11 +9703,21 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return; } final AutofillManager afm = mContext.getSystemService(AutofillManager.class); - if (afm != null) { + if (afm == null) { + return; + } + + if (mLastValueSentToAutofillManager == null + || !mLastValueSentToAutofillManager.equals(mText)) { if (android.view.autofill.Helper.sVerbose) { - Log.v(LOG_TAG, "sendAfterTextChanged(): notify AFM for text=" + mText); + Log.v(LOG_TAG, "notifying AFM after text changed"); } afm.notifyValueChanged(TextView.this); + mLastValueSentToAutofillManager = mText; + } else { + if (android.view.autofill.Helper.sVerbose) { + Log.v(LOG_TAG, "not notifying AFM on unchanged text"); + } } } diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java index b22ce5e2a6ee..f6a69d9aeb93 100644 --- a/core/java/com/android/internal/app/PlatLogoActivity.java +++ b/core/java/com/android/internal/app/PlatLogoActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * 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. @@ -16,157 +16,214 @@ package com.android.internal.app; -import android.animation.Animator; -import android.animation.ObjectAnimator; -import android.annotation.Nullable; +import android.animation.TimeAnimator; import android.app.Activity; -import android.content.ActivityNotFoundException; -import android.content.ContentResolver; -import android.content.Intent; -import android.content.res.ColorStateList; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; -import android.graphics.Outline; import android.graphics.Paint; import android.graphics.Path; -import android.graphics.PixelFormat; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffColorFilter; import android.graphics.drawable.Drawable; -import android.graphics.drawable.GradientDrawable; -import android.graphics.drawable.RippleDrawable; -import android.graphics.drawable.ShapeDrawable; -import android.graphics.drawable.shapes.OvalShape; import android.os.Bundle; -import android.provider.Settings; -import android.util.DisplayMetrics; import android.util.Log; -import android.util.MathUtils; -import android.view.Gravity; -import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.MotionEvent.PointerCoords; import android.view.View; -import android.view.ViewGroup; -import android.view.ViewOutlineProvider; -import android.view.animation.PathInterpolator; import android.widget.FrameLayout; -import android.widget.ImageView; public class PlatLogoActivity extends Activity { - public static final boolean FINISH = true; + FrameLayout layout; + TimeAnimator anim; + PBackground bg; - FrameLayout mLayout; - int mTapCount; - int mKeyCount; - PathInterpolator mInterpolator = new PathInterpolator(0f, 0f, 0.5f, 1f); + private class PBackground extends Drawable { + private float maxRadius, radius, x, y, dp; + private int[] palette; + private int darkest; + private float offset; + + public PBackground() { + randomizePalette(); + } + + /** + * set inner radius of "p" logo + */ + public void setRadius(float r) { + this.radius = Math.max(48*dp, r); + } + + /** + * move the "p" + */ + public void setPosition(float x, float y) { + this.x = x; + this.y = y; + } + + /** + * for animating the "p" + */ + public void setOffset(float o) { + this.offset = o; + } + + /** + * rough luminance calculation + * https://www.w3.org/TR/AERT/#color-contrast + */ + public float lum(int rgb) { + return ((Color.red(rgb) * 299f) + (Color.green(rgb) * 587f) + (Color.blue(rgb) * 114f)) / 1000f; + } + + /** + * create a random evenly-spaced color palette + * guaranteed to contrast! + */ + public void randomizePalette() { + final int slots = 2 + (int)(Math.random() * 2); + float[] color = new float[] { (float) Math.random() * 360f, 1f, 1f }; + palette = new int[slots]; + darkest = 0; + for (int i=0; i<slots; i++) { + palette[i] = Color.HSVToColor(color); + color[0] += 360f/slots; + if (lum(palette[i]) < lum(palette[darkest])) darkest = i; + } + + final StringBuilder str = new StringBuilder(); + for (int c : palette) { + str.append(String.format("#%08x ", c)); + } + Log.v("PlatLogoActivity", "color palette: " + str); + } + + @Override + public void draw(Canvas canvas) { + if (dp == 0) dp = getResources().getDisplayMetrics().density; + final float width = canvas.getWidth(); + final float height = canvas.getHeight(); + if (radius == 0) { + setPosition(width / 2, height / 2); + setRadius(width / 6); + } + final float inner_w = radius * 0.667f; + + final Paint paint = new Paint(); + paint.setStrokeCap(Paint.Cap.BUTT); + canvas.translate(x, y); + + Path p = new Path(); + p.moveTo(-radius, height); + p.lineTo(-radius, 0); + p.arcTo(-radius, -radius, radius, radius, -180, 270, false); + p.lineTo(-radius, radius); + + float w = Math.max(canvas.getWidth(), canvas.getHeight()) * 1.414f; + paint.setStyle(Paint.Style.FILL); + + int i=0; + while (w > radius*2 + inner_w*2) { + paint.setColor(0xFF000000 | palette[i % palette.length]); + // for a slower but more complete version: + // paint.setStrokeWidth(w); + // canvas.drawPath(p, paint); + canvas.drawOval(-w/2, -w/2, w/2, w/2, paint); + w -= inner_w * (1.1f + Math.sin((i/20f + offset) * 3.14159f)); + i++; + } + + // the innermost circle needs to be a constant color to avoid rapid flashing + paint.setColor(0xFF000000 | palette[(darkest+1) % palette.length]); + canvas.drawOval(-radius, -radius, radius, radius, paint); + + p.reset(); + p.moveTo(-radius, height); + p.lineTo(-radius, 0); + p.arcTo(-radius, -radius, radius, radius, -180, 270, false); + p.lineTo(-radius + inner_w, radius); + + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeWidth(inner_w*2); + paint.setColor(palette[darkest]); + canvas.drawPath(p, paint); + paint.setStrokeWidth(inner_w); + paint.setColor(0xFFFFFFFF); + canvas.drawPath(p, paint); + } + + @Override + public void setAlpha(int alpha) { + + } + + @Override + public void setColorFilter(ColorFilter colorFilter) { + + } + + @Override + public int getOpacity() { + return 0; + } + } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mLayout = new FrameLayout(this); - setContentView(mLayout); - } + layout = new FrameLayout(this); + setContentView(layout); - @Override - public void onAttachedToWindow() { - final DisplayMetrics dm = getResources().getDisplayMetrics(); - final float dp = dm.density; - final int size = (int) - (Math.min(Math.min(dm.widthPixels, dm.heightPixels), 600*dp) - 100*dp); - - final ImageView im = new ImageView(this); - final int pad = (int)(40*dp); - im.setPadding(pad, pad, pad, pad); - im.setTranslationZ(20); - im.setScaleX(0.5f); - im.setScaleY(0.5f); - im.setAlpha(0f); - - im.setBackground(new RippleDrawable( - ColorStateList.valueOf(0xFF776677), - getDrawable(com.android.internal.R.drawable.platlogo), - null)); - im.setOutlineProvider(new ViewOutlineProvider() { - @Override - public void getOutline(View view, Outline outline) { - final int w = view.getWidth(); - final int h = view.getHeight(); - outline.setOval((int)(w*.125), (int)(h*.125), (int)(w*.96), (int)(h*.96)); - } - }); - im.setElevation(12f*dp); - im.setClickable(true); - im.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - im.setOnLongClickListener(new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - if (mTapCount < 5) return false; - - final ContentResolver cr = getContentResolver(); - if (Settings.System.getLong(cr, Settings.System.EGG_MODE, 0) - == 0) { - // For posterity: the moment this user unlocked the easter egg - try { - Settings.System.putLong(cr, - Settings.System.EGG_MODE, - System.currentTimeMillis()); - } catch (RuntimeException e) { - Log.e("PlatLogoActivity", "Can't write settings", e); - } - } - im.post(new Runnable() { - @Override - public void run() { - try { - startActivity(new Intent(Intent.ACTION_MAIN) - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_CLEAR_TASK - | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) - .addCategory("com.android.internal.category.PLATLOGO")); - } catch (ActivityNotFoundException ex) { - Log.e("PlatLogoActivity", "No more eggs."); - } - if (FINISH) finish(); - } - }); - return true; - } - }); - mTapCount++; - } - }); + bg = new PBackground(); + layout.setBackground(bg); + + layout.setOnTouchListener(new View.OnTouchListener() { + final PointerCoords pc0 = new PointerCoords(); + final PointerCoords pc1 = new PointerCoords(); - // Enable hardware keyboard input for TV compatibility. - im.setFocusable(true); - im.requestFocus(); - im.setOnKeyListener(new View.OnKeyListener() { @Override - public boolean onKey(View v, int keyCode, KeyEvent event) { - if (keyCode != KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) { - ++mKeyCount; - if (mKeyCount > 2) { - if (mTapCount > 5) { - im.performLongClick(); - } else { - im.performClick(); + public boolean onTouch(View v, MotionEvent event) { + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_MOVE: + if (event.getPointerCount() > 1) { + event.getPointerCoords(0, pc0); + event.getPointerCoords(1, pc1); + bg.setRadius((float) Math.hypot(pc0.x - pc1.x, pc0.y - pc1.y) / 2f); } - } - return true; - } else { - return false; + break; } + return true; } }); + } + + @Override + public void onStart() { + super.onStart(); + + bg.randomizePalette(); - mLayout.addView(im, new FrameLayout.LayoutParams(size, size, Gravity.CENTER)); + anim = new TimeAnimator(); + anim.setTimeListener( + new TimeAnimator.TimeListener() { + @Override + public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) { + bg.setOffset((float) totalTime / 60000f); + bg.invalidateSelf(); + } + }); - im.animate().scaleX(1f).scaleY(1f).alpha(1f) - .setInterpolator(mInterpolator) - .setDuration(500) - .setStartDelay(800) - .start(); + anim.start(); + } + + @Override + public void onStop() { + if (anim != null) { + anim.cancel(); + anim = null; + } + super.onStop(); } } diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java index 6bd693061a85..61aeca679303 100644 --- a/core/java/com/android/internal/app/ResolverListController.java +++ b/core/java/com/android/internal/app/ResolverListController.java @@ -56,9 +56,6 @@ public class ResolverListController { private static final String TAG = "ResolverListController"; private static final boolean DEBUG = false; - Object mLock = new Object(); - - @GuardedBy("mLock") private ResolverComparator mResolverComparator; private boolean isComputed = false; @@ -73,10 +70,8 @@ public class ResolverListController { mLaunchedFromUid = launchedFromUid; mTargetIntent = targetIntent; mReferrerPackage = referrerPackage; - synchronized (mLock) { - mResolverComparator = - new ResolverComparator(mContext, mTargetIntent, mReferrerPackage, null); - } + mResolverComparator = + new ResolverComparator(mContext, mTargetIntent, mReferrerPackage, null); } @VisibleForTesting @@ -244,29 +239,27 @@ public class ResolverListController { @VisibleForTesting @WorkerThread public void sort(List<ResolverActivity.ResolvedComponentInfo> inputList) { - synchronized (mLock) { - if (mResolverComparator == null) { - Log.d(TAG, "Comparator has already been destroyed; skipped."); - return; + if (mResolverComparator == null) { + Log.d(TAG, "Comparator has already been destroyed; skipped."); + return; + } + try { + long beforeRank = System.currentTimeMillis(); + if (!isComputed) { + final CountDownLatch finishComputeSignal = new CountDownLatch(1); + ComputeCallback callback = new ComputeCallback(finishComputeSignal); + mResolverComparator.setCallBack(callback); + mResolverComparator.compute(inputList); + finishComputeSignal.await(); + isComputed = true; } - final CountDownLatch finishComputeSignal = new CountDownLatch(1); - ComputeCallback callback = new ComputeCallback(finishComputeSignal); - mResolverComparator.setCallBack(callback); - try { - long beforeRank = System.currentTimeMillis(); - if (!isComputed) { - mResolverComparator.compute(inputList); - finishComputeSignal.await(); - isComputed = true; - } - Collections.sort(inputList, mResolverComparator); - long afterRank = System.currentTimeMillis(); - if (DEBUG) { - Log.d(TAG, "Time Cost: " + Long.toString(afterRank - beforeRank)); - } - } catch (InterruptedException e) { - Log.e(TAG, "Compute & Sort was interrupted: " + e); + Collections.sort(inputList, mResolverComparator); + long afterRank = System.currentTimeMillis(); + if (DEBUG) { + Log.d(TAG, "Time Cost: " + Long.toString(afterRank - beforeRank)); } + } catch (InterruptedException e) { + Log.e(TAG, "Compute & Sort was interrupted: " + e); } } @@ -287,36 +280,18 @@ public class ResolverListController { @VisibleForTesting public float getScore(ResolverActivity.DisplayResolveInfo target) { - synchronized (mLock) { - if (mResolverComparator == null) { - return 0.0f; - } - return mResolverComparator.getScore(target.getResolvedComponentName()); - } + return mResolverComparator.getScore(target.getResolvedComponentName()); } public void updateModel(ComponentName componentName) { - synchronized (mLock) { - if (mResolverComparator != null) { - mResolverComparator.updateModel(componentName); - } - } + mResolverComparator.updateModel(componentName); } public void updateChooserCounts(String packageName, int userId, String action) { - synchronized (mLock) { - if (mResolverComparator != null) { - mResolverComparator.updateChooserCounts(packageName, userId, action); - } - } + mResolverComparator.updateChooserCounts(packageName, userId, action); } public void destroy() { - synchronized (mLock) { - if (mResolverComparator != null) { - mResolverComparator.destroy(); - } - mResolverComparator = null; - } + mResolverComparator.destroy(); } } diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java index 543bd0c4913d..b049db379341 100644 --- a/core/java/com/android/internal/backup/LocalTransport.java +++ b/core/java/com/android/internal/backup/LocalTransport.java @@ -16,6 +16,7 @@ package com.android.internal.backup; +import android.app.backup.BackupAgent; import android.app.backup.BackupDataInput; import android.app.backup.BackupDataOutput; import android.app.backup.BackupTransport; @@ -98,6 +99,7 @@ public class LocalTransport extends BackupTransport { private FileInputStream mCurFullRestoreStream; private FileOutputStream mFullRestoreSocketStream; private byte[] mFullRestoreBuffer; + private final LocalTransportParameters mParameters; private void makeDataDirs() { mCurrentSetDir.mkdirs(); @@ -105,11 +107,16 @@ public class LocalTransport extends BackupTransport { mCurrentSetIncrementalDir.mkdir(); } - public LocalTransport(Context context) { + public LocalTransport(Context context, LocalTransportParameters parameters) { mContext = context; + mParameters = parameters; makeDataDirs(); } + LocalTransportParameters getParameters() { + return mParameters; + } + @Override public String name() { return new ComponentName(mContext, this.getClass()).flattenToShortString(); @@ -143,6 +150,17 @@ public class LocalTransport extends BackupTransport { } @Override + public int getTransportFlags() { + int flags = super.getTransportFlags(); + // Testing for a fake flag and having it set as a boolean in settings prevents anyone from + // using this it to pull data from the agent + if (mParameters.isFakeEncryptionFlag()) { + flags |= BackupAgent.FLAG_FAKE_CLIENT_SIDE_ENCRYPTION_ENABLED; + } + return flags; + } + + @Override public long requestBackupTime() { // any time is a good time for local backup return 0; diff --git a/core/java/com/android/internal/backup/LocalTransportParameters.java b/core/java/com/android/internal/backup/LocalTransportParameters.java new file mode 100644 index 000000000000..390fae96f810 --- /dev/null +++ b/core/java/com/android/internal/backup/LocalTransportParameters.java @@ -0,0 +1,77 @@ +/* + * 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 com.android.internal.backup; + +import android.content.ContentResolver; +import android.database.ContentObserver; +import android.os.Handler; +import android.provider.Settings; +import android.util.KeyValueListParser; +import android.util.Slog; + +class LocalTransportParameters { + private static final String TAG = "LocalTransportParams"; + private static final String SETTING = Settings.Secure.BACKUP_LOCAL_TRANSPORT_PARAMETERS; + private static final String KEY_FAKE_ENCRYPTION_FLAG = "fake_encryption_flag"; + + private final KeyValueListParser mParser = new KeyValueListParser(','); + private final ContentObserver mObserver; + private final ContentResolver mResolver; + private boolean mFakeEncryptionFlag; + + LocalTransportParameters(Handler handler, ContentResolver resolver) { + mObserver = new Observer(handler); + mResolver = resolver; + } + + /** Observes for changes in the setting. This method MUST be paired with {@link #stop()}. */ + void start() { + mResolver.registerContentObserver(Settings.Secure.getUriFor(SETTING), false, mObserver); + update(); + } + + /** Stop observing for changes in the setting. */ + void stop() { + mResolver.unregisterContentObserver(mObserver); + } + + boolean isFakeEncryptionFlag() { + return mFakeEncryptionFlag; + } + + private void update() { + String parameters = ""; + try { + parameters = Settings.Secure.getString(mResolver, SETTING); + } catch (IllegalArgumentException e) { + Slog.e(TAG, "Malformed " + SETTING + " setting: " + e.getMessage()); + } + mParser.setString(parameters); + mFakeEncryptionFlag = mParser.getBoolean(KEY_FAKE_ENCRYPTION_FLAG, false); + } + + private class Observer extends ContentObserver { + private Observer(Handler handler) { + super(handler); + } + + @Override + public void onChange(boolean selfChange) { + update(); + } + } +} diff --git a/core/java/com/android/internal/backup/LocalTransportService.java b/core/java/com/android/internal/backup/LocalTransportService.java index 77ac31332522..69c48e2a48cf 100644 --- a/core/java/com/android/internal/backup/LocalTransportService.java +++ b/core/java/com/android/internal/backup/LocalTransportService.java @@ -26,8 +26,16 @@ public class LocalTransportService extends Service { @Override public void onCreate() { if (sTransport == null) { - sTransport = new LocalTransport(this); + LocalTransportParameters parameters = + new LocalTransportParameters(getMainThreadHandler(), getContentResolver()); + sTransport = new LocalTransport(this, parameters); } + sTransport.getParameters().start(); + } + + @Override + public void onDestroy() { + sTransport.getParameters().stop(); } @Override diff --git a/core/java/com/android/internal/car/ICarServiceHelper.aidl b/core/java/com/android/internal/car/ICarServiceHelper.aidl deleted file mode 100644 index 9ee330be060b..000000000000 --- a/core/java/com/android/internal/car/ICarServiceHelper.aidl +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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 com.android.internal.car; - -/** - * Helper API for car service. Only for itneraction between system server and car service. - * @hide - */ -interface ICarServiceHelper { -} diff --git a/core/java/com/android/internal/colorextraction/drawable/GradientDrawable.java b/core/java/com/android/internal/colorextraction/drawable/GradientDrawable.java index 500c028ed4c6..bf151c39271a 100644 --- a/core/java/com/android/internal/colorextraction/drawable/GradientDrawable.java +++ b/core/java/com/android/internal/colorextraction/drawable/GradientDrawable.java @@ -57,6 +57,8 @@ public class GradientDrawable extends Drawable { private int mMainColor; private int mSecondaryColor; private ValueAnimator mColorAnimation; + private int mMainColorTo; + private int mSecondaryColorTo; public GradientDrawable(@NonNull Context context) { mDensity = context.getResources().getDisplayMetrics().density; @@ -76,7 +78,7 @@ public class GradientDrawable extends Drawable { } public void setColors(int mainColor, int secondaryColor, boolean animated) { - if (mainColor == mMainColor && secondaryColor == mSecondaryColor) { + if (mainColor == mMainColorTo && secondaryColor == mSecondaryColorTo) { return; } @@ -84,6 +86,9 @@ public class GradientDrawable extends Drawable { mColorAnimation.cancel(); } + mMainColorTo = mainColor; + mSecondaryColorTo = mainColor; + if (animated) { final int mainFrom = mMainColor; final int secFrom = mSecondaryColor; diff --git a/core/java/com/android/internal/colorextraction/types/Tonal.java b/core/java/com/android/internal/colorextraction/types/Tonal.java index 9b7383fcbe8a..7b25a0691c40 100644 --- a/core/java/com/android/internal/colorextraction/types/Tonal.java +++ b/core/java/com/android/internal/colorextraction/types/Tonal.java @@ -405,12 +405,13 @@ public class Tonal implements ExtractionType { return v - (float) Math.floor(v); } - static class TonalPalette { - final float[] h; - final float[] s; - final float[] l; - final float minHue; - final float maxHue; + @VisibleForTesting + public static class TonalPalette { + public final float[] h; + public final float[] s; + public final float[] l; + public final float minHue; + public final float maxHue; TonalPalette(float[] h, float[] s, float[] l) { if (h.length != s.length || s.length != l.length) { diff --git a/core/java/com/android/internal/notification/SystemNotificationChannels.java b/core/java/com/android/internal/notification/SystemNotificationChannels.java index 4a181b27b2e3..44adbb22eb7e 100644 --- a/core/java/com/android/internal/notification/SystemNotificationChannels.java +++ b/core/java/com/android/internal/notification/SystemNotificationChannels.java @@ -49,6 +49,7 @@ public class SystemNotificationChannels { public static String USB = "USB"; public static String FOREGROUND_SERVICE = "FOREGROUND_SERVICE"; public static String HEAVY_WEIGHT_APP = "HEAVY_WEIGHT_APP"; + public static String SYSTEM_CHANGES = "SYSTEM_CHANGES"; public static void createAll(Context context) { final NotificationManager nm = context.getSystemService(NotificationManager.class); @@ -152,6 +153,11 @@ public class SystemNotificationChannels { .build()); channelsList.add(heavyWeightChannel); + NotificationChannel systemChanges = new NotificationChannel(SYSTEM_CHANGES, + context.getString(R.string.notification_channel_system_changes), + NotificationManager.IMPORTANCE_LOW); + channelsList.add(systemChanges); + nm.createNotificationChannels(channelsList); } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 40dcf25bbd10..4e515918a0cd 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -187,7 +187,7 @@ public class BatteryStatsImpl extends BatteryStats { public final AtomicFile mCheckinFile; public final AtomicFile mDailyFile; - static final int MSG_UPDATE_WAKELOCKS = 1; + static final int MSG_REPORT_CPU_UPDATE_NEEDED = 1; static final int MSG_REPORT_POWER_CHANGE = 2; static final int MSG_REPORT_CHARGING = 3; static final long DELAY_UPDATE_WAKELOCKS = 5*1000; @@ -230,6 +230,13 @@ public class BatteryStatsImpl extends BatteryStats { @VisibleForTesting protected final SparseIntArray mPendingUids = new SparseIntArray(); + @GuardedBy("this") + private long mNumCpuTimeReads; + @GuardedBy("this") + private long mNumBatchedCpuTimeReads; + @GuardedBy("this") + private long mCpuTimeReadsTrackingStartTime = SystemClock.uptimeMillis(); + /** Container for Resource Power Manager stats. Updated by updateRpmStatsLocked. */ private final RpmStats mTmpRpmStats = new RpmStats(); /** The soonest the RPM stats can be updated after it was last updated. */ @@ -273,10 +280,7 @@ public class BatteryStatsImpl extends BatteryStats { public void handleMessage(Message msg) { BatteryCallback cb = mCallback; switch (msg.what) { - case MSG_UPDATE_WAKELOCKS: - synchronized (BatteryStatsImpl.this) { - updateCpuTimeLocked(); - } + case MSG_REPORT_CPU_UPDATE_NEEDED: if (cb != null) { cb.batteryNeedsCpuUpdate(); } @@ -302,6 +306,10 @@ public class BatteryStatsImpl extends BatteryStats { } } + public void postBatteryNeedsCpuUpdateMsg() { + mHandler.sendEmptyMessage(MSG_REPORT_CPU_UPDATE_NEEDED); + } + /** * Update per-freq cpu times for all the uids in {@link #mPendingUids}. */ @@ -484,9 +492,14 @@ public class BatteryStatsImpl extends BatteryStats { Future<?> scheduleSync(String reason, int flags); Future<?> scheduleCpuSyncDueToRemovedUid(int uid); - Future<?> scheduleReadProcStateCpuTimes(boolean onBattery, boolean onBatteryScreenOff); + Future<?> scheduleReadProcStateCpuTimes(boolean onBattery, boolean onBatteryScreenOff, + long delayMillis); Future<?> scheduleCopyFromAllUidsCpuTimes(boolean onBattery, boolean onBatteryScreenOff); Future<?> scheduleCpuSyncDueToSettingChange(); + Future<?> scheduleCpuSyncDueToScreenStateChange(boolean onBattery, + boolean onBatteryScreenOff); + Future<?> scheduleCpuSyncDueToWakelockChange(long delayMillis); + void cancelCpuSyncDueToWakelockChange(); } public Handler mHandler; @@ -678,7 +691,8 @@ public class BatteryStatsImpl extends BatteryStats { StopwatchTimer mCameraOnTimer; int mGpsSignalQualityBin = -1; - final StopwatchTimer[] mGpsSignalQualityTimer = + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + protected final StopwatchTimer[] mGpsSignalQualityTimer = new StopwatchTimer[GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS]; int mPhoneSignalStrengthBin = -1; @@ -760,6 +774,8 @@ public class BatteryStatsImpl extends BatteryStats { @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) protected StopwatchTimer mBluetoothScanTimer; + boolean mIsCellularTxPowerHigh = false; + int mMobileRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW; long mMobileRadioActiveStartTime; StopwatchTimer mMobileRadioActiveTimer; @@ -1450,12 +1466,10 @@ public class BatteryStatsImpl extends BatteryStats { long mCount; long mLoadedCount; long mUnpluggedCount; - long mPluggedCount; LongSamplingCounter(TimeBase timeBase, Parcel in) { mTimeBase = timeBase; - mPluggedCount = in.readLong(); - mCount = mPluggedCount; + mCount = in.readLong(); mLoadedCount = in.readLong(); mUnpluggedCount = in.readLong(); timeBase.add(this); @@ -1474,16 +1488,15 @@ public class BatteryStatsImpl extends BatteryStats { @Override public void onTimeStarted(long elapsedRealtime, long baseUptime, long baseRealtime) { - mUnpluggedCount = mPluggedCount; + mUnpluggedCount = mCount; } @Override public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) { - mPluggedCount = mCount; } public long getCountLocked(int which) { - long val = mTimeBase.isRunning() ? mCount : mPluggedCount; + long val = mCount; if (which == STATS_SINCE_UNPLUGGED) { val -= mUnpluggedCount; } else if (which != STATS_SINCE_CHARGED) { @@ -1496,12 +1509,15 @@ public class BatteryStatsImpl extends BatteryStats { public void logState(Printer pw, String prefix) { pw.println(prefix + "mCount=" + mCount + " mLoadedCount=" + mLoadedCount - + " mUnpluggedCount=" + mUnpluggedCount - + " mPluggedCount=" + mPluggedCount); + + " mUnpluggedCount=" + mUnpluggedCount); } void addCountLocked(long count) { - if (mTimeBase.isRunning()) { + addCountLocked(count, mTimeBase.isRunning()); + } + + void addCountLocked(long count, boolean isRunning) { + if (isRunning) { mCount += count; } } @@ -1511,7 +1527,7 @@ public class BatteryStatsImpl extends BatteryStats { */ void reset(boolean detachIfReset) { mCount = 0; - mLoadedCount = mPluggedCount = mUnpluggedCount = 0; + mLoadedCount = mUnpluggedCount = 0; if (detachIfReset) { detach(); } @@ -1528,7 +1544,7 @@ public class BatteryStatsImpl extends BatteryStats { void readSummaryFromParcelLocked(Parcel in) { mLoadedCount = in.readLong(); mCount = mLoadedCount; - mUnpluggedCount = mPluggedCount = mLoadedCount; + mUnpluggedCount = mLoadedCount; } } @@ -3522,7 +3538,7 @@ public class BatteryStatsImpl extends BatteryStats { mHistoryLastWritten.cmd = HistoryItem.CMD_NULL; } - void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) { + void addHistoryBufferLocked(long elapsedRealtimeMs, HistoryItem cur) { if (!mHaveBatteryLevel || !mRecordingHistory) { return; } @@ -3603,8 +3619,8 @@ public class BatteryStatsImpl extends BatteryStats { } else if (dataSize >= MAX_HISTORY_BUFFER) { if (!mHistoryOverflow) { mHistoryOverflow = true; - addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur); - addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_OVERFLOW, cur); + addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur); + addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_OVERFLOW, cur); return; } @@ -3642,7 +3658,7 @@ public class BatteryStatsImpl extends BatteryStats { return; } - addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur); + addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur); return; } @@ -3650,15 +3666,14 @@ public class BatteryStatsImpl extends BatteryStats { // The history is currently empty; we need it to start with a time stamp. cur.currentTime = System.currentTimeMillis(); if (recordResetDueToOverflow) { - addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_OVERFLOW, cur); + addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_OVERFLOW, cur); } - addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_RESET, cur); + addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_RESET, cur); } - addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur); + addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur); } - private void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, byte cmd, - HistoryItem cur) { + private void addHistoryBufferLocked(long elapsedRealtimeMs, byte cmd, HistoryItem cur) { if (mIteratingHistory) { throw new IllegalStateException("Can't do this while iterating history!"); } @@ -3692,17 +3707,17 @@ public class BatteryStatsImpl extends BatteryStats { mHistoryAddTmp.wakeReasonTag = null; mHistoryAddTmp.eventCode = HistoryItem.EVENT_NONE; mHistoryAddTmp.states &= ~HistoryItem.STATE_CPU_RUNNING_FLAG; - addHistoryRecordInnerLocked(wakeElapsedTime, uptimeMs, mHistoryAddTmp); + addHistoryRecordInnerLocked(wakeElapsedTime, mHistoryAddTmp); } } mHistoryCur.states |= HistoryItem.STATE_CPU_RUNNING_FLAG; mTrackRunningHistoryElapsedRealtime = elapsedRealtimeMs; mTrackRunningHistoryUptime = uptimeMs; - addHistoryRecordInnerLocked(elapsedRealtimeMs, uptimeMs, mHistoryCur); + addHistoryRecordInnerLocked(elapsedRealtimeMs, mHistoryCur); } - void addHistoryRecordInnerLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) { - addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, cur); + void addHistoryRecordInnerLocked(long elapsedRealtimeMs, HistoryItem cur) { + addHistoryBufferLocked(elapsedRealtimeMs, cur); if (!USE_OLD_HISTORY) { return; @@ -3743,7 +3758,7 @@ public class BatteryStatsImpl extends BatteryStats { if (mNumHistoryItems == MAX_HISTORY_ITEMS || mNumHistoryItems == MAX_MAX_HISTORY_ITEMS) { - addHistoryRecordLocked(elapsedRealtimeMs, HistoryItem.CMD_OVERFLOW); + addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_OVERFLOW, cur); } if (mNumHistoryItems >= MAX_HISTORY_ITEMS) { @@ -3760,7 +3775,7 @@ public class BatteryStatsImpl extends BatteryStats { } } - addHistoryRecordLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE); + addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur); } public void addHistoryEventLocked(long elapsedRealtimeMs, long uptimeMs, int code, @@ -3826,6 +3841,7 @@ public class BatteryStatsImpl extends BatteryStats { mActiveHistoryStates2 = 0xffffffff; } + @GuardedBy("this") public void updateTimeBasesLocked(boolean unplugged, int screenState, long uptime, long realtime) { final boolean screenOff = !isScreenOn(screenState); @@ -3849,9 +3865,6 @@ public class BatteryStatsImpl extends BatteryStats { + Display.stateToString(screenState) + " and battery is " + (unplugged ? "on" : "off")); } - updateCpuTimeLocked(); - mExternalSync.scheduleCopyFromAllUidsCpuTimes(mOnBatteryTimeBase.isRunning(), - mOnBatteryScreenOffTimeBase.isRunning()); mOnBatteryTimeBase.setRunning(unplugged, uptime, realtime); if (updateOnBatteryTimeBase) { @@ -3903,6 +3916,7 @@ public class BatteryStatsImpl extends BatteryStats { * This should only be called after the cpu times have been read. * @see #scheduleRemoveIsolatedUidLocked(int, int) */ + @GuardedBy("this") public void removeIsolatedUidLocked(int isolatedUid) { StatsLog.write( StatsLog.ISOLATED_UID_CHANGED, mIsolatedUids.get(isolatedUid, -1), @@ -4139,15 +4153,11 @@ public class BatteryStatsImpl extends BatteryStats { } private void requestWakelockCpuUpdate() { - if (!mHandler.hasMessages(MSG_UPDATE_WAKELOCKS)) { - Message m = mHandler.obtainMessage(MSG_UPDATE_WAKELOCKS); - mHandler.sendMessageDelayed(m, DELAY_UPDATE_WAKELOCKS); - } + mExternalSync.scheduleCpuSyncDueToWakelockChange(DELAY_UPDATE_WAKELOCKS); } private void requestImmediateCpuUpdate() { - mHandler.removeMessages(MSG_UPDATE_WAKELOCKS); - mHandler.sendEmptyMessage(MSG_UPDATE_WAKELOCKS); + mExternalSync.scheduleCpuSyncDueToWakelockChange(0 /* delayMillis */); } public void setRecordAllHistoryLocked(boolean enabled) { @@ -4550,7 +4560,7 @@ public class BatteryStatsImpl extends BatteryStats { } public boolean startAddingCpuLocked() { - mHandler.removeMessages(MSG_UPDATE_WAKELOCKS); + mExternalSync.cancelCpuSyncDueToWakelockChange(); return mOnBatteryInternal; } @@ -4732,6 +4742,7 @@ public class BatteryStatsImpl extends BatteryStats { return; } + @GuardedBy("this") public void noteScreenStateLocked(int state) { state = mPretendScreenOff ? Display.STATE_OFF : state; @@ -4802,6 +4813,8 @@ public class BatteryStatsImpl extends BatteryStats { + Display.stateToString(state)); addHistoryRecordLocked(elapsedRealtime, uptime); } + mExternalSync.scheduleCpuSyncDueToScreenStateChange( + mOnBatteryTimeBase.isRunning(), mOnBatteryScreenOffTimeBase.isRunning()); if (isScreenOn(state)) { updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), state, mClocks.uptimeMillis() * 1000, elapsedRealtime * 1000); @@ -5281,6 +5294,18 @@ public class BatteryStatsImpl extends BatteryStats { case TelephonyManager.NETWORK_TYPE_HSPAP: bin = DATA_CONNECTION_HSPAP; break; + case TelephonyManager.NETWORK_TYPE_GSM: + bin = DATA_CONNECTION_GSM; + break; + case TelephonyManager.NETWORK_TYPE_TD_SCDMA: + bin = DATA_CONNECTION_TD_SCDMA; + break; + case TelephonyManager.NETWORK_TYPE_IWLAN: + bin = DATA_CONNECTION_IWLAN; + break; + case TelephonyManager.NETWORK_TYPE_LTE_CA: + bin = DATA_CONNECTION_LTE_CA; + break; default: bin = DATA_CONNECTION_OTHER; break; @@ -9191,8 +9216,14 @@ public class BatteryStatsImpl extends BatteryStats { } public void addCpuTimeLocked(int utime, int stime) { - mUserTime += utime; - mSystemTime += stime; + addCpuTimeLocked(utime, stime, mBsi.mOnBatteryTimeBase.isRunning()); + } + + public void addCpuTimeLocked(int utime, int stime, boolean isRunning) { + if (isRunning) { + mUserTime += utime; + mSystemTime += stime; + } } public void addForegroundTimeLocked(long ttime) { @@ -9648,6 +9679,7 @@ public class BatteryStatsImpl extends BatteryStats { return ps; } + @GuardedBy("mBsi") public void updateUidProcessStateLocked(int procState) { int uidRunningState; // Make special note of Foreground Services @@ -9670,7 +9702,11 @@ public class BatteryStatsImpl extends BatteryStats { if (mBsi.mPendingUids.size() == 0) { mBsi.mExternalSync.scheduleReadProcStateCpuTimes( mBsi.mOnBatteryTimeBase.isRunning(), - mBsi.mOnBatteryScreenOffTimeBase.isRunning()); + mBsi.mOnBatteryScreenOffTimeBase.isRunning(), + mBsi.mConstants.PROC_STATE_CPU_TIMES_READ_DELAY_MS); + mBsi.mNumCpuTimeReads++; + } else { + mBsi.mNumBatchedCpuTimeReads++; } if (mBsi.mPendingUids.indexOfKey(mUid) < 0 || ArrayUtils.contains(CRITICAL_PROC_STATES, mProcessState)) { @@ -9931,8 +9967,6 @@ public class BatteryStatsImpl extends BatteryStats { if (wl != null) { StopwatchTimer wlt = getWakelockTimerLocked(wl, type); wlt.stopRunningLocked(elapsedRealtimeMs); - if (!wlt.isRunningLocked()) { // only tell statsd if truly stopped - } } if (type == WAKE_TYPE_PARTIAL) { if (mAggregatedPartialWakelockTimer != null) { @@ -11206,6 +11240,25 @@ public class BatteryStatsImpl extends BatteryStats { private ModemActivityInfo mLastModemActivityInfo = new ModemActivityInfo(0, 0, 0, new int[0], 0, 0); + private ModemActivityInfo getDeltaModemActivityInfo(ModemActivityInfo activityInfo) { + if (activityInfo == null) { + return null; + } + int[] txTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS]; + for (int i = 0; i < ModemActivityInfo.TX_POWER_LEVELS; i++) { + txTimeMs[i] = activityInfo.getTxTimeMillis()[i] + - mLastModemActivityInfo.getTxTimeMillis()[i]; + } + ModemActivityInfo deltaInfo = new ModemActivityInfo(activityInfo.getTimestamp(), + activityInfo.getSleepTimeMillis() - mLastModemActivityInfo.getSleepTimeMillis(), + activityInfo.getIdleTimeMillis() - mLastModemActivityInfo.getIdleTimeMillis(), + txTimeMs, + activityInfo.getRxTimeMillis() - mLastModemActivityInfo.getRxTimeMillis(), + activityInfo.getEnergyUsed() - mLastModemActivityInfo.getEnergyUsed()); + mLastModemActivityInfo = activityInfo; + return deltaInfo; + } + /** * Distribute Cell radio energy info and network traffic to apps. */ @@ -11213,6 +11266,10 @@ public class BatteryStatsImpl extends BatteryStats { if (DEBUG_ENERGY) { Slog.d(TAG, "Updating mobile radio stats with " + activityInfo); } + ModemActivityInfo deltaInfo = getDeltaModemActivityInfo(activityInfo); + + // Add modem tx power to history. + addModemTxPowerToHistory(deltaInfo); // Grab a separate lock to acquire the network stats, which may do I/O. NetworkStats delta = null; @@ -11226,22 +11283,6 @@ public class BatteryStatsImpl extends BatteryStats { } } - int rxTimeMs = 0; - int[] txTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS]; - int idleTimeMs = 0; - int sleepTimeMs = 0; - if (activityInfo != null) { - rxTimeMs = activityInfo.getRxTimeMillis() - mLastModemActivityInfo.getRxTimeMillis(); - for (int i = 0; i < ModemActivityInfo.TX_POWER_LEVELS; i++) { - txTimeMs[i] = activityInfo.getTxTimeMillis()[i] - - mLastModemActivityInfo.getTxTimeMillis()[i]; - } - idleTimeMs = - activityInfo.getIdleTimeMillis() - mLastModemActivityInfo.getIdleTimeMillis(); - sleepTimeMs = - activityInfo.getSleepTimeMillis() - mLastModemActivityInfo.getSleepTimeMillis(); - } - synchronized (this) { if (!mOnBatteryInternal) { if (delta != null) { @@ -11250,14 +11291,14 @@ public class BatteryStatsImpl extends BatteryStats { return; } - if (activityInfo != null) { + if (deltaInfo != null) { mHasModemReporting = true; mModemActivity.getIdleTimeCounter().addCountLocked( - idleTimeMs); - mModemActivity.getRxTimeCounter().addCountLocked(rxTimeMs); + deltaInfo.getIdleTimeMillis()); + mModemActivity.getRxTimeCounter().addCountLocked(deltaInfo.getRxTimeMillis()); for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) { mModemActivity.getTxTimeCounters()[lvl] - .addCountLocked(txTimeMs[lvl]); + .addCountLocked(deltaInfo.getTxTimeMillis()[lvl]); } // POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V. @@ -11265,16 +11306,17 @@ public class BatteryStatsImpl extends BatteryStats { PowerProfile.POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE) / 1000.0; if (opVolt != 0) { double energyUsed = - sleepTimeMs * + deltaInfo.getSleepTimeMillis() * mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_SLEEP) - + idleTimeMs * + + deltaInfo.getIdleTimeMillis() * mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_IDLE) - + rxTimeMs * + + deltaInfo.getRxTimeMillis() * mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX); + int[] txTimeMs = deltaInfo.getTxTimeMillis(); for (int i = 0; i < Math.min(txTimeMs.length, - SignalStrength.NUM_SIGNAL_STRENGTH_BINS); i++) { + SignalStrength.NUM_SIGNAL_STRENGTH_BINS); i++) { energyUsed += txTimeMs[i] * mPowerProfile.getAveragePower( - PowerProfile.POWER_MODEM_CONTROLLER_TX, i); + PowerProfile.POWER_MODEM_CONTROLLER_TX, i); } // We store the power drain as mAms. @@ -11350,11 +11392,11 @@ public class BatteryStatsImpl extends BatteryStats { radioTime -= appRadioTime; totalPackets -= appPackets; - if (activityInfo != null) { + if (deltaInfo != null) { ControllerActivityCounterImpl activityCounter = u.getOrCreateModemControllerActivityLocked(); if (totalRxPackets > 0 && entry.rxPackets > 0) { - final long rxMs = (entry.rxPackets * rxTimeMs) + final long rxMs = (entry.rxPackets * deltaInfo.getRxTimeMillis()) / totalRxPackets; activityCounter.getRxTimeCounter().addCountLocked(rxMs); } @@ -11362,7 +11404,7 @@ public class BatteryStatsImpl extends BatteryStats { if (totalTxPackets > 0 && entry.txPackets > 0) { for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) { long txMs = - entry.txPackets * txTimeMs[lvl]; + entry.txPackets * deltaInfo.getTxTimeMillis()[lvl]; txMs /= totalTxPackets; activityCounter.getTxTimeCounters()[lvl].addCountLocked(txMs); } @@ -11388,6 +11430,44 @@ public class BatteryStatsImpl extends BatteryStats { new BluetoothActivityEnergyInfo(0, 0, 0, 0, 0, 0); /** + * Add modem tx power to history + * Device is said to be in high cellular transmit power when it has spent most of the transmit + * time at the highest power level. + * @param activityInfo + */ + private void addModemTxPowerToHistory(final ModemActivityInfo activityInfo) { + if (activityInfo == null) { + return; + } + int[] txTimeMs = activityInfo.getTxTimeMillis(); + if (txTimeMs == null || txTimeMs.length != ModemActivityInfo.TX_POWER_LEVELS) { + return; + } + final long elapsedRealtime = mClocks.elapsedRealtime(); + final long uptime = mClocks.uptimeMillis(); + int levelMaxTimeSpent = 0; + for (int i = 1; i < txTimeMs.length; i++) { + if (txTimeMs[i] > txTimeMs[levelMaxTimeSpent]) { + levelMaxTimeSpent = i; + } + } + if (levelMaxTimeSpent == ModemActivityInfo.TX_POWER_LEVELS - 1) { + if (!mIsCellularTxPowerHigh) { + mHistoryCur.states2 |= HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG; + addHistoryRecordLocked(elapsedRealtime, uptime); + mIsCellularTxPowerHigh = true; + } + return; + } + if (mIsCellularTxPowerHigh) { + mHistoryCur.states2 &= ~HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG; + addHistoryRecordLocked(elapsedRealtime, uptime); + mIsCellularTxPowerHigh = false; + } + return; + } + + /** * Distribute Bluetooth energy info and network traffic to apps. * @param info The energy information from the bluetooth controller. */ @@ -11716,12 +11796,24 @@ public class BatteryStatsImpl extends BatteryStats { } } + public boolean isOnBatteryLocked() { + return mOnBatteryTimeBase.isRunning(); + } + + public boolean isOnBatteryScreenOffLocked() { + return mOnBatteryScreenOffTimeBase.isRunning(); + } + /** * Read and distribute CPU usage across apps. If their are partial wakelocks being held * and we are on battery with screen off, we give more of the cpu time to those apps holding * wakelocks. If the screen is on, we just assign the actual cpu time an app used. + * It's possible this will be invoked after the internal battery/screen states are updated, so + * passing the appropriate battery/screen states to try attribute the cpu times to correct + * buckets. */ - public void updateCpuTimeLocked() { + @GuardedBy("this") + public void updateCpuTimeLocked(boolean onBattery, boolean onBatteryScreenOff) { if (mPowerProfile == null) { return; } @@ -11738,7 +11830,7 @@ public class BatteryStatsImpl extends BatteryStats { // usually holding the wakelock on behalf of an app. // And Only distribute cpu power to wakelocks if the screen is off and we're on battery. ArrayList<StopwatchTimer> partialTimersToConsider = null; - if (mOnBatteryScreenOffTimeBase.isRunning()) { + if (onBatteryScreenOff) { partialTimersToConsider = new ArrayList<>(); for (int i = mPartialTimers.size() - 1; i >= 0; --i) { final StopwatchTimer timer = mPartialTimers.get(i); @@ -11756,7 +11848,7 @@ public class BatteryStatsImpl extends BatteryStats { // When the battery is not on, we don't attribute the cpu times to any timers but we still // need to take the snapshots. - if (!mOnBatteryInternal) { + if (!onBattery) { mKernelUidCpuTimeReader.readDelta(null); mKernelUidCpuFreqTimeReader.readDelta(null); if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) { @@ -11772,16 +11864,16 @@ public class BatteryStatsImpl extends BatteryStats { mUserInfoProvider.refreshUserIds(); final SparseLongArray updatedUids = mKernelUidCpuFreqTimeReader.perClusterTimesAvailable() ? null : new SparseLongArray(); - readKernelUidCpuTimesLocked(partialTimersToConsider, updatedUids); + readKernelUidCpuTimesLocked(partialTimersToConsider, updatedUids, onBattery); // updatedUids=null means /proc/uid_time_in_state provides snapshots of per-cluster cpu // freqs, so no need to approximate these values. if (updatedUids != null) { - updateClusterSpeedTimes(updatedUids); + updateClusterSpeedTimes(updatedUids, onBattery); } - readKernelUidCpuFreqTimesLocked(partialTimersToConsider); + readKernelUidCpuFreqTimesLocked(partialTimersToConsider, onBattery, onBatteryScreenOff); if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) { - readKernelUidCpuActiveTimesLocked(); - readKernelUidCpuClusterTimesLocked(); + readKernelUidCpuActiveTimesLocked(onBattery); + readKernelUidCpuClusterTimesLocked(onBattery); } } @@ -11821,7 +11913,7 @@ public class BatteryStatsImpl extends BatteryStats { * @param updatedUids The uids for which times spent at different frequencies are calculated. */ @VisibleForTesting - public void updateClusterSpeedTimes(@NonNull SparseLongArray updatedUids) { + public void updateClusterSpeedTimes(@NonNull SparseLongArray updatedUids, boolean onBattery) { long totalCpuClustersTimeMs = 0; // Read the time spent for each cluster at various cpu frequencies. final long[][] clusterSpeedTimesMs = new long[mKernelCpuSpeedReaders.length][]; @@ -11863,7 +11955,7 @@ public class BatteryStatsImpl extends BatteryStats { } cpuSpeeds[speed].addCountLocked(appCpuTimeUs * clusterSpeedTimesMs[cluster][speed] - / totalCpuClustersTimeMs); + / totalCpuClustersTimeMs, onBattery); } } } @@ -11880,7 +11972,7 @@ public class BatteryStatsImpl extends BatteryStats { */ @VisibleForTesting public void readKernelUidCpuTimesLocked(@Nullable ArrayList<StopwatchTimer> partialTimers, - @Nullable SparseLongArray updatedUids) { + @Nullable SparseLongArray updatedUids, boolean onBattery) { mTempTotalCpuUserTimeUs = mTempTotalCpuSystemTimeUs = 0; final int numWakelocks = partialTimers == null ? 0 : partialTimers.size(); final long startTimeMs = mClocks.uptimeMillis(); @@ -11931,8 +12023,8 @@ public class BatteryStatsImpl extends BatteryStats { Slog.d(TAG, sb.toString()); } - u.mUserCpuTime.addCountLocked(userTimeUs); - u.mSystemCpuTime.addCountLocked(systemTimeUs); + u.mUserCpuTime.addCountLocked(userTimeUs, onBattery); + u.mSystemCpuTime.addCountLocked(systemTimeUs, onBattery); if (updatedUids != null) { updatedUids.put(u.getUid(), userTimeUs + systemTimeUs); } @@ -11964,15 +12056,15 @@ public class BatteryStatsImpl extends BatteryStats { Slog.d(TAG, sb.toString()); } - timer.mUid.mUserCpuTime.addCountLocked(userTimeUs); - timer.mUid.mSystemCpuTime.addCountLocked(systemTimeUs); + timer.mUid.mUserCpuTime.addCountLocked(userTimeUs, onBattery); + timer.mUid.mSystemCpuTime.addCountLocked(systemTimeUs, onBattery); if (updatedUids != null) { final int uid = timer.mUid.getUid(); updatedUids.put(uid, updatedUids.get(uid, 0) + userTimeUs + systemTimeUs); } final Uid.Proc proc = timer.mUid.getProcessStatsLocked("*wakelock*"); - proc.addCpuTimeLocked(userTimeUs / 1000, systemTimeUs / 1000); + proc.addCpuTimeLocked(userTimeUs / 1000, systemTimeUs / 1000, onBattery); mTempTotalCpuUserTimeUs -= userTimeUs; mTempTotalCpuSystemTimeUs -= systemTimeUs; @@ -11987,7 +12079,8 @@ public class BatteryStatsImpl extends BatteryStats { * @param partialTimers The wakelock holders among which the cpu freq times will be distributed. */ @VisibleForTesting - public void readKernelUidCpuFreqTimesLocked(@Nullable ArrayList<StopwatchTimer> partialTimers) { + public void readKernelUidCpuFreqTimesLocked(@Nullable ArrayList<StopwatchTimer> partialTimers, + boolean onBattery, boolean onBatteryScreenOff) { final boolean perClusterTimesAvailable = mKernelUidCpuFreqTimeReader.perClusterTimesAvailable(); final int numWakelocks = partialTimers == null ? 0 : partialTimers.size(); @@ -12010,13 +12103,13 @@ public class BatteryStatsImpl extends BatteryStats { if (u.mCpuFreqTimeMs == null || u.mCpuFreqTimeMs.getSize() != cpuFreqTimeMs.length) { u.mCpuFreqTimeMs = new LongSamplingCounterArray(mOnBatteryTimeBase); } - u.mCpuFreqTimeMs.addCountLocked(cpuFreqTimeMs); + u.mCpuFreqTimeMs.addCountLocked(cpuFreqTimeMs, onBattery); if (u.mScreenOffCpuFreqTimeMs == null || u.mScreenOffCpuFreqTimeMs.getSize() != cpuFreqTimeMs.length) { u.mScreenOffCpuFreqTimeMs = new LongSamplingCounterArray( mOnBatteryScreenOffTimeBase); } - u.mScreenOffCpuFreqTimeMs.addCountLocked(cpuFreqTimeMs); + u.mScreenOffCpuFreqTimeMs.addCountLocked(cpuFreqTimeMs, onBatteryScreenOff); if (perClusterTimesAvailable) { if (u.mCpuClusterSpeedTimesUs == null || @@ -12052,7 +12145,7 @@ public class BatteryStatsImpl extends BatteryStats { } else { appAllocationUs = cpuFreqTimeMs[freqIndex] * 1000; } - cpuTimesUs[speed].addCountLocked(appAllocationUs); + cpuTimesUs[speed].addCountLocked(appAllocationUs, onBattery); freqIndex++; } } @@ -12086,7 +12179,7 @@ public class BatteryStatsImpl extends BatteryStats { } final long allocationUs = mWakeLockAllocationsUs[cluster][speed] / (numWakelocks - i); - cpuTimeUs[speed].addCountLocked(allocationUs); + cpuTimeUs[speed].addCountLocked(allocationUs, onBattery); mWakeLockAllocationsUs[cluster][speed] -= allocationUs; } } @@ -12099,7 +12192,7 @@ public class BatteryStatsImpl extends BatteryStats { * counters. */ @VisibleForTesting - public void readKernelUidCpuActiveTimesLocked() { + public void readKernelUidCpuActiveTimesLocked(boolean onBattery) { final long startTimeMs = mClocks.uptimeMillis(); mKernelUidCpuActiveTimeReader.readDelta((uid, cpuActiveTimesUs) -> { uid = mapUid(uid); @@ -12114,7 +12207,7 @@ public class BatteryStatsImpl extends BatteryStats { return; } final Uid u = getUidStatsLocked(uid); - u.mCpuActiveTimeMs.addCountLocked(cpuActiveTimesUs); + u.mCpuActiveTimeMs.addCountLocked(cpuActiveTimesUs, onBattery); }); final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs; @@ -12128,7 +12221,7 @@ public class BatteryStatsImpl extends BatteryStats { * counters. */ @VisibleForTesting - public void readKernelUidCpuClusterTimesLocked() { + public void readKernelUidCpuClusterTimesLocked(boolean onBattery) { final long startTimeMs = mClocks.uptimeMillis(); mKernelUidCpuClusterTimeReader.readDelta((uid, cpuClusterTimesUs) -> { uid = mapUid(uid); @@ -12143,7 +12236,7 @@ public class BatteryStatsImpl extends BatteryStats { return; } final Uid u = getUidStatsLocked(uid); - u.mCpuClusterTimesMs.addCountLocked(cpuClusterTimesUs); + u.mCpuClusterTimesMs.addCountLocked(cpuClusterTimesUs, onBattery); }); final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs; @@ -12166,6 +12259,7 @@ public class BatteryStatsImpl extends BatteryStats { return false; } + @GuardedBy("this") protected void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime, final boolean onBattery, final int oldStatus, final int level, final int chargeUAh) { boolean doWrite = false; @@ -12307,7 +12401,7 @@ public class BatteryStatsImpl extends BatteryStats { boolean reset) { mRecordingHistory = true; mHistoryCur.currentTime = System.currentTimeMillis(); - addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, + addHistoryBufferLocked(elapsedRealtimeMs, reset ? HistoryItem.CMD_RESET : HistoryItem.CMD_CURRENT_TIME, mHistoryCur); mHistoryCur.currentTime = 0; @@ -12320,8 +12414,7 @@ public class BatteryStatsImpl extends BatteryStats { final long uptimeMs) { if (mRecordingHistory) { mHistoryCur.currentTime = currentTime; - addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_CURRENT_TIME, - mHistoryCur); + addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_CURRENT_TIME, mHistoryCur); mHistoryCur.currentTime = 0; } } @@ -12329,8 +12422,7 @@ public class BatteryStatsImpl extends BatteryStats { private void recordShutdownLocked(final long elapsedRealtimeMs, final long uptimeMs) { if (mRecordingHistory) { mHistoryCur.currentTime = System.currentTimeMillis(); - addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_SHUTDOWN, - mHistoryCur); + addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_SHUTDOWN, mHistoryCur); mHistoryCur.currentTime = 0; } } @@ -12344,6 +12436,7 @@ public class BatteryStatsImpl extends BatteryStats { // This should probably be exposed in the API, though it's not critical public static final int BATTERY_PLUGGED_NONE = OsProtoEnums.BATTERY_PLUGGED_NONE; // = 0 + @GuardedBy("this") public void setBatteryStateLocked(final int status, final int health, final int plugType, final int level, /* not final */ int temp, final int volt, final int chargeUAh, final int chargeFullUAh) { @@ -12353,9 +12446,7 @@ public class BatteryStatsImpl extends BatteryStats { reportChangesToStatsLog(mHaveBatteryLevel ? mHistoryCur : null, status, plugType, level, temp); - final boolean onBattery = - plugType == BATTERY_PLUGGED_NONE && - status != BatteryManager.BATTERY_STATUS_UNKNOWN; + final boolean onBattery = isOnBattery(plugType, status); final long uptime = mClocks.uptimeMillis(); final long elapsedRealtime = mClocks.elapsedRealtime(); if (!mHaveBatteryLevel) { @@ -12545,6 +12636,10 @@ public class BatteryStatsImpl extends BatteryStats { mMaxLearnedBatteryCapacity = Math.max(mMaxLearnedBatteryCapacity, chargeFullUAh); } + public static boolean isOnBattery(int plugType, int status) { + return plugType == BATTERY_PLUGGED_NONE && status != BatteryManager.BATTERY_STATUS_UNKNOWN; + } + // Inform StatsLog of setBatteryState changes. // If this is the first reporting, pass in recentPast == null. private void reportChangesToStatsLog(HistoryItem recentPast, @@ -13089,15 +13184,19 @@ public class BatteryStatsImpl extends BatteryStats { = "track_cpu_active_cluster_time"; public static final String KEY_READ_BINARY_CPU_TIME = "read_binary_cpu_time"; + public static final String KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS + = "proc_state_cpu_times_read_delay_ms"; private static final boolean DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE = true; private static final boolean DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME = true; private static final boolean DEFAULT_READ_BINARY_CPU_TIME = false; + private static final long DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS = 5_000; public boolean TRACK_CPU_TIMES_BY_PROC_STATE = DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE; public boolean TRACK_CPU_ACTIVE_CLUSTER_TIME = DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME; // Not used right now. public boolean READ_BINARY_CPU_TIME = DEFAULT_READ_BINARY_CPU_TIME; + public long PROC_STATE_CPU_TIMES_READ_DELAY_MS = DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS; private ContentResolver mResolver; private final KeyValueListParser mParser = new KeyValueListParser(','); @@ -13137,7 +13236,9 @@ public class BatteryStatsImpl extends BatteryStats { KEY_TRACK_CPU_ACTIVE_CLUSTER_TIME, DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME); READ_BINARY_CPU_TIME = mParser.getBoolean( KEY_READ_BINARY_CPU_TIME, DEFAULT_READ_BINARY_CPU_TIME); - + updateProcStateCpuTimesReadDelayMs(PROC_STATE_CPU_TIMES_READ_DELAY_MS, + mParser.getLong(KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS, + DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS)); } } @@ -13146,6 +13247,19 @@ public class BatteryStatsImpl extends BatteryStats { if (isEnabled && !wasEnabled) { mKernelSingleUidTimeReader.markDataAsStale(true); mExternalSync.scheduleCpuSyncDueToSettingChange(); + + mNumCpuTimeReads = 0; + mNumBatchedCpuTimeReads = 0; + mCpuTimeReadsTrackingStartTime = mClocks.uptimeMillis(); + } + } + + private void updateProcStateCpuTimesReadDelayMs(long oldDelayMillis, long newDelayMillis) { + PROC_STATE_CPU_TIMES_READ_DELAY_MS = newDelayMillis; + if (oldDelayMillis != newDelayMillis) { + mNumCpuTimeReads = 0; + mNumBatchedCpuTimeReads = 0; + mCpuTimeReadsTrackingStartTime = mClocks.uptimeMillis(); } } @@ -13156,9 +13270,12 @@ public class BatteryStatsImpl extends BatteryStats { pw.println(TRACK_CPU_ACTIVE_CLUSTER_TIME); pw.print(KEY_READ_BINARY_CPU_TIME); pw.print("="); pw.println(READ_BINARY_CPU_TIME); + pw.print(KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS); pw.print("="); + pw.println(PROC_STATE_CPU_TIMES_READ_DELAY_MS); } } + @GuardedBy("this") public void dumpConstantsLocked(PrintWriter pw) { mConstants.dumpLocked(pw); } @@ -13274,7 +13391,7 @@ public class BatteryStatsImpl extends BatteryStats { if (USE_OLD_HISTORY) { addHistoryRecordLocked(elapsedRealtime, uptime, HistoryItem.CMD_START, mHistoryCur); } - addHistoryBufferLocked(elapsedRealtime, uptime, HistoryItem.CMD_START, mHistoryCur); + addHistoryBufferLocked(elapsedRealtime, HistoryItem.CMD_START, mHistoryCur); startRecordingHistory(elapsedRealtime, uptime, false); } @@ -13542,6 +13659,7 @@ public class BatteryStatsImpl extends BatteryStats { mCameraOnTimer.readSummaryFromParcelLocked(in); mBluetoothScanNesting = 0; mBluetoothScanTimer.readSummaryFromParcelLocked(in); + mIsCellularTxPowerHigh = false; int NRPMS = in.readInt(); if (NRPMS > 10000) { @@ -14478,6 +14596,7 @@ public class BatteryStatsImpl extends BatteryStats { mCameraOnTimer = new StopwatchTimer(mClocks, null, -13, null, mOnBatteryTimeBase, in); mBluetoothScanNesting = 0; mBluetoothScanTimer = new StopwatchTimer(mClocks, null, -14, null, mOnBatteryTimeBase, in); + mIsCellularTxPowerHigh = false; mDischargeUnplugLevel = in.readInt(); mDischargePlugLevel = in.readInt(); mDischargeCurrentLevel = in.readInt(); @@ -14862,5 +14981,11 @@ public class BatteryStatsImpl extends BatteryStats { mCameraOnTimer.logState(pr, " "); } super.dumpLocked(context, pw, flags, reqUid, histStart); + pw.print("Total cpu time reads: "); + pw.println(mNumCpuTimeReads); + pw.print("Batched cpu time reads: "); + pw.println(mNumBatchedCpuTimeReads); + pw.print("Batching Duration (min): "); + pw.println((mClocks.uptimeMillis() - mCpuTimeReadsTrackingStartTime) / (60 * 1000)); } } diff --git a/core/java/com/android/internal/os/FuseAppLoop.java b/core/java/com/android/internal/os/FuseAppLoop.java index 088e7268b984..12405ebce057 100644 --- a/core/java/com/android/internal/os/FuseAppLoop.java +++ b/core/java/com/android/internal/os/FuseAppLoop.java @@ -283,6 +283,7 @@ public class FuseAppLoop implements Handler.Callback { return -OsConstants.EBADF; } + @GuardedBy("mLock") private CallbackEntry getCallbackEntryOrThrowLocked(long inode) throws ErrnoException { final CallbackEntry entry = mCallbackMap.get(checkInode(inode)); if (entry == null) { @@ -291,12 +292,14 @@ public class FuseAppLoop implements Handler.Callback { return entry; } + @GuardedBy("mLock") private void recycleLocked(Args args) { if (mArgsPool.size() < ARGS_POOL_SIZE) { mArgsPool.add(args); } } + @GuardedBy("mLock") private void replySimpleLocked(long unique, int result) { if (mInstance != 0) { native_replySimple(mInstance, unique, result); diff --git a/core/java/com/android/internal/os/KernelSingleUidTimeReader.java b/core/java/com/android/internal/os/KernelSingleUidTimeReader.java index ebeb24c41479..42839171dc53 100644 --- a/core/java/com/android/internal/os/KernelSingleUidTimeReader.java +++ b/core/java/com/android/internal/os/KernelSingleUidTimeReader.java @@ -16,6 +16,7 @@ package com.android.internal.os; import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; +import static com.android.internal.os.KernelUidCpuFreqTimeReader.UID_TIMES_PROC_FILE; import android.annotation.NonNull; import android.util.Slog; @@ -54,6 +55,12 @@ public class KernelSingleUidTimeReader { private boolean mSingleUidCpuTimesAvailable = true; @GuardedBy("this") private boolean mHasStaleData; + // We use the freq count obtained from /proc/uid_time_in_state to decide how many longs + // to read from each /proc/uid/<uid>/time_in_state. On the first read, verify if this is + // correct and if not, set {@link #mSingleUidCpuTimesAvailable} to false. This flag will + // indicate whether we checked for validity or not. + @GuardedBy("this") + private boolean mCpuFreqsCountVerified; private final Injector mInjector; @@ -82,15 +89,15 @@ public class KernelSingleUidTimeReader { final String procFile = new StringBuilder(PROC_FILE_DIR) .append(uid) .append(PROC_FILE_NAME).toString(); - final long[] cpuTimesMs = new long[mCpuFreqsCount]; + final long[] cpuTimesMs; try { final byte[] data = mInjector.readData(procFile); + if (!mCpuFreqsCountVerified) { + verifyCpuFreqsCount(data.length, procFile); + } final ByteBuffer buffer = ByteBuffer.wrap(data); buffer.order(ByteOrder.nativeOrder()); - for (int i = 0; i < mCpuFreqsCount; ++i) { - // Times read will be in units of 10ms - cpuTimesMs[i] = buffer.getLong() * 10; - } + cpuTimesMs = readCpuTimesFromByteBuffer(buffer); } catch (Exception e) { if (++mReadErrorCounter >= TOTAL_READ_ERROR_COUNT) { mSingleUidCpuTimesAvailable = false; @@ -103,6 +110,27 @@ public class KernelSingleUidTimeReader { } } + private void verifyCpuFreqsCount(int numBytes, String procFile) { + final int actualCount = (numBytes / Long.BYTES); + if (mCpuFreqsCount != actualCount) { + mSingleUidCpuTimesAvailable = false; + throw new IllegalStateException("Freq count didn't match," + + "count from " + UID_TIMES_PROC_FILE + "=" + mCpuFreqsCount + ", but" + + "count from " + procFile + "=" + actualCount); + } + mCpuFreqsCountVerified = true; + } + + private long[] readCpuTimesFromByteBuffer(ByteBuffer buffer) { + final long[] cpuTimesMs; + cpuTimesMs = new long[mCpuFreqsCount]; + for (int i = 0; i < mCpuFreqsCount; ++i) { + // Times read will be in units of 10ms + cpuTimesMs[i] = buffer.getLong() * 10; + } + return cpuTimesMs; + } + /** * Compute and return cpu times delta of an uid using previously read cpu times and * {@param latestCpuTimesMs}. diff --git a/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java index b8982cce91b5..d97538c30498 100644 --- a/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java +++ b/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java @@ -49,7 +49,7 @@ import java.io.IOException; public class KernelUidCpuFreqTimeReader { private static final boolean DEBUG = false; private static final String TAG = "KernelUidCpuFreqTimeReader"; - private static final String UID_TIMES_PROC_FILE = "/proc/uid_time_in_state"; + static final String UID_TIMES_PROC_FILE = "/proc/uid_time_in_state"; public interface Callback { void onUidCpuFreqTime(int uid, long[] cpuFreqTimeMs); diff --git a/core/java/com/android/internal/os/KernelUidCpuTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java index 65615c0ffb02..444049e7e415 100644 --- a/core/java/com/android/internal/os/KernelUidCpuTimeReader.java +++ b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java @@ -78,10 +78,11 @@ public class KernelUidCpuTimeReader { final long userTimeUs = Long.parseLong(splitter.next(), 10); final long systemTimeUs = Long.parseLong(splitter.next(), 10); + boolean notifyCallback = false; + long userTimeDeltaUs = userTimeUs; + long systemTimeDeltaUs = systemTimeUs; // Only report if there is a callback and if this is not the first read. if (callback != null && mLastTimeReadUs != 0) { - long userTimeDeltaUs = userTimeUs; - long systemTimeDeltaUs = systemTimeUs; int index = mLastUserTimeUs.indexOfKey(uid); if (index >= 0) { userTimeDeltaUs -= mLastUserTimeUs.valueAt(index); @@ -114,12 +115,13 @@ public class KernelUidCpuTimeReader { } } - if (userTimeDeltaUs != 0 || systemTimeDeltaUs != 0) { - callback.onUidCpuTime(uid, userTimeDeltaUs, systemTimeDeltaUs); - } + notifyCallback = (userTimeDeltaUs != 0 || systemTimeDeltaUs != 0); } mLastUserTimeUs.put(uid, userTimeUs); mLastSystemTimeUs.put(uid, systemTimeUs); + if (notifyCallback) { + callback.onUidCpuTime(uid, userTimeDeltaUs, systemTimeDeltaUs); + } } } catch (IOException e) { Slog.e(TAG, "Failed to read uid_cputime: " + e.getMessage()); diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index 66475e445efa..bb5a0ad86dd4 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -71,10 +71,11 @@ public class RuntimeInit { public void uncaughtException(Thread t, Throwable e) { // Don't re-enter if KillApplicationHandler has already run if (mCrashing) return; - if (mApplicationObject == null) { - // The "FATAL EXCEPTION" string is still used on Android even though - // apps can set a custom UncaughtExceptionHandler that renders uncaught - // exceptions non-fatal. + + // mApplicationObject is null for non-zygote java programs (e.g. "am") + // There are also apps running with the system UID. We don't want the + // first clause in either of these two cases, only for system_server. + if (mApplicationObject == null && (Process.SYSTEM_UID == Process.myUid())) { Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e); } else { StringBuilder message = new StringBuilder(); @@ -229,7 +230,7 @@ public class RuntimeInit { * @param argv Argument vector for main() * @param classLoader the classLoader to load {@className} with */ - private static Runnable findStaticMain(String className, String[] argv, + protected static Runnable findStaticMain(String className, String[] argv, ClassLoader classLoader) { Class<?> cl; diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java index cadb66ae6e08..b38c851e5101 100644 --- a/core/java/com/android/internal/os/WebViewZygoteInit.java +++ b/core/java/com/android/internal/os/WebViewZygoteInit.java @@ -129,7 +129,7 @@ class WebViewZygoteInit { final Runnable caller; try { - sServer.registerServerSocket("webview_zygote"); + sServer.registerServerSocketFromEnv("webview_zygote"); // The select loop returns early in the child process after a fork and // loops forever in the zygote. caller = sServer.runSelectLoop(TextUtils.join(",", Build.SUPPORTED_ABIS)); diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index e69a36064693..28a7c1204071 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -53,8 +53,8 @@ public final class Zygote { public static final int DISABLE_VERIFIER = 1 << 9; /** Only use oat files located in /system. Otherwise use dex/jar/apk . */ public static final int ONLY_USE_SYSTEM_OAT_FILES = 1 << 10; - /** Do not enfore hidden API access restrictions. */ - public static final int DISABLE_HIDDEN_API_CHECKS = 1 << 11; + /** Do enfore hidden API access restrictions. */ + public static final int ENABLE_HIDDEN_API_CHECKS = 1 << 11; /** Force generation of native debugging information for backtraces. */ public static final int DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 12; @@ -69,6 +69,13 @@ public final class Zygote { private static final ZygoteHooks VM_HOOKS = new ZygoteHooks(); + /** + * An extraArg passed when a zygote process is forking a child-zygote, specifying a name + * in the abstract socket namespace. This socket name is what the new child zygote + * should listen for connections on. + */ + public static final String CHILD_ZYGOTE_SOCKET_NAME_ARG = "--zygote-socket="; + private Zygote() {} /** Called for some security initialization before any fork. */ @@ -100,6 +107,8 @@ public final class Zygote { * @param fdsToIgnore null-ok an array of ints, either null or holding * one or more POSIX file descriptor numbers that are to be ignored * in the file descriptor table check. + * @param startChildZygote if true, the new child process will itself be a + * new zygote process. * @param instructionSet null-ok the instruction set to use. * @param appDataDir null-ok the data directory of the app. * @@ -108,13 +117,13 @@ public final class Zygote { */ public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose, - int[] fdsToIgnore, String instructionSet, String appDataDir) { + int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir) { VM_HOOKS.preFork(); // Resets nice priority for zygote process. resetNicePriority(); int pid = nativeForkAndSpecialize( uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose, - fdsToIgnore, instructionSet, appDataDir); + fdsToIgnore, startChildZygote, instructionSet, appDataDir); // Enable tracing as soon as possible for the child process. if (pid == 0) { Trace.setTracingEnabled(true, runtimeFlags); @@ -128,7 +137,7 @@ public final class Zygote { native private static int nativeForkAndSpecialize(int uid, int gid, int[] gids,int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose, - int[] fdsToIgnore, String instructionSet, String appDataDir); + int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir); /** * Called to do any initialization before starting an application. @@ -160,9 +169,6 @@ public final class Zygote { */ public static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags, int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) { - // SystemServer is always allowed to use hidden APIs. - runtimeFlags |= DISABLE_HIDDEN_API_CHECKS; - VM_HOOKS.preFork(); // Resets nice priority for zygote process. resetNicePriority(); @@ -191,8 +197,8 @@ public final class Zygote { native protected static void nativeUnmountStorageOnInit(); private static void callPostForkChildHooks(int runtimeFlags, boolean isSystemServer, - String instructionSet) { - VM_HOOKS.postForkChild(runtimeFlags, isSystemServer, instructionSet); + boolean isZygote, String instructionSet) { + VM_HOOKS.postForkChild(runtimeFlags, isSystemServer, isZygote, instructionSet); } /** diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index 6a87b1f4d3fd..a32fb4316d12 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -221,8 +221,8 @@ class ZygoteConnection { pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids, parsedArgs.runtimeFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo, - parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.instructionSet, - parsedArgs.appDataDir); + parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.startChildZygote, + parsedArgs.instructionSet, parsedArgs.appDataDir); try { if (pid == 0) { @@ -233,7 +233,8 @@ class ZygoteConnection { IoUtils.closeQuietly(serverPipeFd); serverPipeFd = null; - return handleChildProc(parsedArgs, descriptors, childPipeFd); + return handleChildProc(parsedArgs, descriptors, childPipeFd, + parsedArgs.startChildZygote); } else { // In the parent. A pid < 0 indicates a failure and will be handled in // handleParentProc. @@ -415,6 +416,14 @@ class ZygoteConnection { boolean preloadDefault; /** + * Whether this is a request to start a zygote process as a child of this zygote. + * Set with --start-child-zygote. The remaining arguments must include the + * CHILD_ZYGOTE_SOCKET_NAME_ARG flag to indicate the abstract socket name that + * should be used for communication. + */ + boolean startChildZygote; + + /** * Constructs instance and parses args * @param args zygote command-line args * @throws IllegalArgumentException @@ -565,6 +574,8 @@ class ZygoteConnection { preloadPackageCacheKey = args[++curArg]; } else if (arg.equals("--preload-default")) { preloadDefault = true; + } else if (arg.equals("--start-child-zygote")) { + startChildZygote = true; } else { break; } @@ -587,6 +598,20 @@ class ZygoteConnection { remainingArgs = new String[args.length - curArg]; System.arraycopy(args, curArg, remainingArgs, 0, remainingArgs.length); } + + if (startChildZygote) { + boolean seenChildSocketArg = false; + for (String arg : remainingArgs) { + if (arg.startsWith(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG)) { + seenChildSocketArg = true; + break; + } + } + if (!seenChildSocketArg) { + throw new IllegalArgumentException("--start-child-zygote specified " + + "without " + Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG); + } + } } } @@ -739,9 +764,10 @@ class ZygoteConnection { * @param parsedArgs non-null; zygote args * @param descriptors null-ok; new file descriptors for stdio if available. * @param pipeFd null-ok; pipe for communication back to Zygote. + * @param isZygote whether this new child process is itself a new Zygote. */ private Runnable handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors, - FileDescriptor pipeFd) { + FileDescriptor pipeFd, boolean isZygote) { /** * By the time we get here, the native code has closed the two actual Zygote * socket connections, and substituted /dev/null in their place. The LocalSocket @@ -778,8 +804,13 @@ class ZygoteConnection { // Should not get here. throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned"); } else { - return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, - null /* classLoader */); + if (!isZygote) { + return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, + null /* classLoader */); + } else { + return ZygoteInit.childZygoteInit(parsedArgs.targetSdkVersion, + parsedArgs.remainingArgs, null /* classLoader */); + } } } diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 74802c8bdf79..c5d0a04b5deb 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -98,10 +98,6 @@ public class ZygoteInit { private static final String SOCKET_NAME_ARG = "--socket-name="; - /* Dexopt flag to disable hidden API access checks when dexopting SystemServer. - * Must be kept in sync with com.android.server.pm.Installer. */ - private static final int DEXOPT_DISABLE_HIDDEN_API_CHECKS = 1 << 10; - /** * Used to pre-load resources. */ @@ -569,10 +565,7 @@ public class ZygoteInit { if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) { final String packageName = "*"; final String outputPath = null; - // Dexopt with a flag which lifts restrictions on hidden API usage. - // Offending methods would otherwise be re-verified at runtime and - // we want to avoid the performance overhead of that. - final int dexFlags = DEXOPT_DISABLE_HIDDEN_API_CHECKS; + final int dexFlags = 0; final String compilerFilter = systemServerFilter; final String uuid = StorageManager.UUID_PRIVATE_INTERNAL; final String seInfo = null; @@ -583,7 +576,8 @@ public class ZygoteInit { installd.dexopt(classPathElement, Process.SYSTEM_UID, packageName, instructionSet, dexoptNeeded, outputPath, dexFlags, compilerFilter, uuid, classLoaderContext, seInfo, false /* downgrade */, - targetSdkVersion, /*profileName*/ null, /*dexMetadataPath*/ null); + targetSdkVersion, /*profileName*/ null, /*dexMetadataPath*/ null, + "server-dexopt"); } catch (RemoteException | ServiceSpecificException e) { // Ignore (but log), we need this on the classpath for fallback mode. Log.w(TAG, "Failed compiling classpath element for system server: " @@ -762,7 +756,7 @@ public class ZygoteInit { throw new RuntimeException("No ABI list supplied."); } - zygoteServer.registerServerSocket(socketName); + zygoteServer.registerServerSocketFromEnv(socketName); // In some configurations, we avoid preloading resources and classes eagerly. // In such cases, we will preload things prior to our first fork. if (!enableLazyPreload) { @@ -877,5 +871,16 @@ public class ZygoteInit { return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader); } + /** + * The main function called when starting a child zygote process. This is used as an + * alternative to zygoteInit(), which skips calling into initialization routines that + * start the Binder threadpool. + */ + static final Runnable childZygoteInit( + int targetSdkVersion, String[] argv, ClassLoader classLoader) { + RuntimeInit.Arguments args = new RuntimeInit.Arguments(argv); + return RuntimeInit.findStaticMain(args.startClass, args.startArgs, classLoader); + } + private static final native void nativeZygoteInit(); } diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java index 8baa15a058de..fecf9b9da5dd 100644 --- a/core/java/com/android/internal/os/ZygoteServer.java +++ b/core/java/com/android/internal/os/ZygoteServer.java @@ -44,9 +44,21 @@ class ZygoteServer { private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_"; + /** + * Listening socket that accepts new server connections. + */ private LocalServerSocket mServerSocket; /** + * Whether or not mServerSocket's underlying FD should be closed directly. + * If mServerSocket is created with an existing FD, closing the socket does + * not close the FD and it must be closed explicitly. If the socket is created + * with a name instead, then closing the socket will close the underlying FD + * and it should not be double-closed. + */ + private boolean mCloseSocketFd; + + /** * Set by the child process, immediately after a call to {@code Zygote.forkAndSpecialize}. */ private boolean mIsForkChild; @@ -59,11 +71,12 @@ class ZygoteServer { } /** - * Registers a server socket for zygote command connections + * Registers a server socket for zygote command connections. This locates the server socket + * file descriptor through an ANDROID_SOCKET_ environment variable. * * @throws RuntimeException when open fails */ - void registerServerSocket(String socketName) { + void registerServerSocketFromEnv(String socketName) { if (mServerSocket == null) { int fileDesc; final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName; @@ -78,6 +91,7 @@ class ZygoteServer { FileDescriptor fd = new FileDescriptor(); fd.setInt$(fileDesc); mServerSocket = new LocalServerSocket(fd); + mCloseSocketFd = true; } catch (IOException ex) { throw new RuntimeException( "Error binding to local socket '" + fileDesc + "'", ex); @@ -86,6 +100,22 @@ class ZygoteServer { } /** + * Registers a server socket for zygote command connections. This opens the server socket + * at the specified name in the abstract socket namespace. + */ + void registerServerSocketAtAbstractName(String socketName) { + if (mServerSocket == null) { + try { + mServerSocket = new LocalServerSocket(socketName); + mCloseSocketFd = false; + } catch (IOException ex) { + throw new RuntimeException( + "Error binding to abstract socket '" + socketName + "'", ex); + } + } + } + + /** * Waits for and accepts a single command connection. Throws * RuntimeException on failure. */ @@ -112,7 +142,7 @@ class ZygoteServer { if (mServerSocket != null) { FileDescriptor fd = mServerSocket.getFileDescriptor(); mServerSocket.close(); - if (fd != null) { + if (fd != null && mCloseSocketFd) { Os.close(fd); } } @@ -219,6 +249,11 @@ class ZygoteServer { Log.e(TAG, "Caught post-fork exception in child process.", e); throw e; } + } finally { + // Reset the child flag, in the event that the child process is a child- + // zygote. The flag will not be consulted this loop pass after the Runnable + // is returned. + mIsForkChild = false; } } } diff --git a/core/java/com/android/internal/print/DumpUtils.java b/core/java/com/android/internal/print/DumpUtils.java index 1916c11e9c9d..f44a1d122f39 100644 --- a/core/java/com/android/internal/print/DumpUtils.java +++ b/core/java/com/android/internal/print/DumpUtils.java @@ -213,10 +213,9 @@ public class DumpUtils { PrintAttributes.MediaSize mediaSize = attributes.getMediaSize(); if (mediaSize != null) { writeMediaSize(context, proto, "media_size", PrintAttributesProto.MEDIA_SIZE, mediaSize); + proto.write("is_portrait", PrintAttributesProto.IS_PORTRAIT, attributes.isPortrait()); } - proto.write("is_portrait", PrintAttributesProto.IS_PORTRAIT, attributes.isPortrait()); - PrintAttributes.Resolution res = attributes.getResolution(); if (res != null) { writeResolution(proto, "resolution", PrintAttributesProto.RESOLUTION, res); diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index f70d3c2a27fd..221bf8823b74 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -35,7 +35,7 @@ oneway interface IStatusBar void animateCollapsePanels(); void togglePanel(); - void showChargingAnimation(int batteryLevel); + void showWirelessChargingAnimation(int batteryLevel); /** * Notifies the status bar of a System UI visibility flag change. diff --git a/core/java/com/android/internal/util/OWNERS b/core/java/com/android/internal/util/OWNERS new file mode 100644 index 000000000000..21d750c59a4e --- /dev/null +++ b/core/java/com/android/internal/util/OWNERS @@ -0,0 +1,24 @@ +per-file AsyncChannel*=lorenzo@google.com +per-file AsyncChannel*=satk@google.com +per-file AsyncChannel*=silberst@google.com +per-file BitUtils*=ek@google.com +per-file BitUtils*=lorenzo@google.com +per-file BitUtils*=satk@google.com +per-file MessageUtils*=ek@google.com +per-file MessageUtils*=lorenzo@google.com +per-file MessageUtils*=satk@google.com +per-file Protocol*=ek@google.com +per-file Protocol*=lorenzo@google.com +per-file Protocol*=quiche@google.com +per-file Protocol*=satk@google.com +per-file Protocol*=silberst@google.com +per-file RingBuffer*=ek@google.com +per-file RingBuffer*=lorenzo@google.com +per-file RingBuffer*=satk@google.com +per-file State*=ek@google.com +per-file State*=lorenzo@google.com +per-file State*=quiche@google.com +per-file State*=silberst@google.com +per-file TokenBucket*=ek@google.com +per-file TokenBucket*=lorenzo@google.com +per-file TokenBucket*=satk@google.com diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java index b53459e072d4..67dc81af5895 100644 --- a/core/java/com/android/internal/view/menu/MenuBuilder.java +++ b/core/java/com/android/internal/view/menu/MenuBuilder.java @@ -551,6 +551,7 @@ public class MenuBuilder implements Menu { mPreventDispatchingItemsChanged = true; clear(); clearHeader(); + mPresenters.clear(); mPreventDispatchingItemsChanged = false; mItemsChangedWhileDispatchPrevented = false; onItemsChanged(true); diff --git a/core/java/com/android/internal/widget/LocalImageResolver.java b/core/java/com/android/internal/widget/LocalImageResolver.java new file mode 100644 index 000000000000..71d3bb5d6b5c --- /dev/null +++ b/core/java/com/android/internal/widget/LocalImageResolver.java @@ -0,0 +1,76 @@ +/* + * 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 com.android.internal.widget; + +import android.annotation.Nullable; +import android.app.Notification; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.net.Uri; + +import java.io.IOException; +import java.io.InputStream; + +/** + * A class to extract Bitmaps from a MessagingStyle message. + */ +public class LocalImageResolver { + + private static final int MAX_SAFE_ICON_SIZE_PX = 480; + + @Nullable + public static Drawable resolveImage(Uri uri, Context context) throws IOException { + BitmapFactory.Options onlyBoundsOptions = getBoundsOptionsForImage(uri, context); + if ((onlyBoundsOptions.outWidth == -1) || (onlyBoundsOptions.outHeight == -1)) { + return null; + } + + int originalSize = + (onlyBoundsOptions.outHeight > onlyBoundsOptions.outWidth) + ? onlyBoundsOptions.outHeight + : onlyBoundsOptions.outWidth; + + double ratio = (originalSize > MAX_SAFE_ICON_SIZE_PX) + ? (originalSize / MAX_SAFE_ICON_SIZE_PX) + : 1.0; + + BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); + bitmapOptions.inSampleSize = getPowerOfTwoForSampleRatio(ratio); + InputStream input = context.getContentResolver().openInputStream(uri); + Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions); + input.close(); + return new BitmapDrawable(context.getResources(), bitmap); + } + + private static BitmapFactory.Options getBoundsOptionsForImage(Uri uri, Context context) + throws IOException { + InputStream input = context.getContentResolver().openInputStream(uri); + BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options(); + onlyBoundsOptions.inJustDecodeBounds = true; + BitmapFactory.decodeStream(input, null, onlyBoundsOptions); + input.close(); + return onlyBoundsOptions; + } + + private static int getPowerOfTwoForSampleRatio(double ratio) { + int k = Integer.highestOneBit((int) Math.floor(ratio)); + return Math.max(1, k); + } +} diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java index 5577d6e86fee..239beaa220e0 100644 --- a/core/java/com/android/internal/widget/MessagingGroup.java +++ b/core/java/com/android/internal/widget/MessagingGroup.java @@ -22,9 +22,12 @@ import android.annotation.Nullable; import android.annotation.StyleRes; import android.app.Notification; import android.content.Context; +import android.graphics.Point; +import android.graphics.Rect; import android.graphics.drawable.Icon; import android.text.TextUtils; import android.util.AttributeSet; +import android.util.DisplayMetrics; import android.util.Pools; import android.view.LayoutInflater; import android.view.View; @@ -61,6 +64,11 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou private boolean mIsHidingAnimated; private boolean mNeedsGeneratedAvatar; private Notification.Person mSender; + private boolean mAvatarsAtEnd; + private ViewGroup mImageContainer; + private MessagingImageMessage mIsolatedMessage; + private boolean mTransformingImages; + private Point mDisplaySize = new Point(); public MessagingGroup(@NonNull Context context) { super(context); @@ -87,6 +95,35 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou mSenderName = findViewById(R.id.message_name); mSenderName.addOnLayoutChangeListener(MessagingLayout.MESSAGING_PROPERTY_ANIMATOR); mAvatarView = findViewById(R.id.message_icon); + mImageContainer = findViewById(R.id.messaging_group_icon_container); + DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); + mDisplaySize.x = displayMetrics.widthPixels; + mDisplaySize.y = displayMetrics.heightPixels; + } + + public void updateClipRect() { + // We want to clip to the senderName if it's available, otherwise our images will come + // from a weird position + Rect clipRect; + if (mSenderName.getVisibility() != View.GONE && !mTransformingImages) { + ViewGroup parent = (ViewGroup) mSenderName.getParent(); + int top = getDistanceFromParent(mSenderName, parent) - getDistanceFromParent( + mMessageContainer, parent) + mSenderName.getHeight(); + clipRect = new Rect(0, top, mDisplaySize.x, mDisplaySize.y); + } else { + clipRect = null; + } + mMessageContainer.setClipBounds(clipRect); + } + + private int getDistanceFromParent(View searchedView, ViewGroup parent) { + int position = 0; + View view = searchedView; + while(view != parent) { + position += view.getTop() + view.getTranslationY(); + view = (View) view.getParent(); + } + return position; } public void setSender(Notification.Person sender, CharSequence nameOverride) { @@ -129,12 +166,14 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou } public void removeMessage(MessagingMessage messagingMessage) { - mMessageContainer.removeView(messagingMessage); + ViewGroup messageParent = (ViewGroup) messagingMessage.getView().getParent(); + messageParent.removeView(messagingMessage.getView()); Runnable recycleRunnable = () -> { - mMessageContainer.removeTransientView(messagingMessage); + messageParent.removeTransientView(messagingMessage.getView()); messagingMessage.recycle(); if (mMessageContainer.getChildCount() == 0 - && mMessageContainer.getTransientViewCount() == 0) { + && mMessageContainer.getTransientViewCount() == 0 + && mImageContainer.getChildCount() == 0) { ViewParent parent = getParent(); if (parent instanceof ViewGroup) { ((ViewGroup) parent).removeView(MessagingGroup.this); @@ -148,9 +187,10 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou } }; if (isShown()) { - mMessageContainer.addTransientView(messagingMessage, 0); - performRemoveAnimation(messagingMessage, recycleRunnable); - if (mMessageContainer.getChildCount() == 0) { + messageParent.addTransientView(messagingMessage.getView(), 0); + performRemoveAnimation(messagingMessage.getView(), recycleRunnable); + if (mMessageContainer.getChildCount() == 0 + && mImageContainer.getChildCount() == 0) { removeGroupAnimated(null); } } else { @@ -160,12 +200,8 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou } private void removeGroupAnimated(Runnable endAction) { - MessagingPropertyAnimator.fadeOut(mAvatarView, null); - MessagingPropertyAnimator.startLocalTranslationTo(mAvatarView, - (int) (-getHeight() * 0.5f), MessagingLayout.FAST_OUT_LINEAR_IN); - MessagingPropertyAnimator.fadeOut(mSenderName, null); - MessagingPropertyAnimator.startLocalTranslationTo(mSenderName, - (int) (-getHeight() * 0.5f), MessagingLayout.FAST_OUT_LINEAR_IN); + performRemoveAnimation(mAvatarView, null); + performRemoveAnimation(mSenderName, null); boolean endActionTriggered = false; for (int i = mMessageContainer.getChildCount() - 1; i >= 0; i--) { View child = mMessageContainer.getChildAt(i); @@ -182,14 +218,17 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou performRemoveAnimation(child, childEndAction); endActionTriggered = true; } + if (mIsolatedMessage != null) { + performRemoveAnimation(mIsolatedMessage, !endActionTriggered ? endAction : null); + endActionTriggered = true; + } if (!endActionTriggered && endAction != null) { endAction.run(); } } - public void performRemoveAnimation(View message, - Runnable recycleRunnable) { - MessagingPropertyAnimator.fadeOut(message, recycleRunnable); + public void performRemoveAnimation(View message, Runnable endAction) { + MessagingPropertyAnimator.fadeOut(message, endAction); MessagingPropertyAnimator.startLocalTranslationTo(message, (int) (-getHeight() * 0.5f), MessagingLayout.FAST_OUT_LINEAR_IN); } @@ -222,6 +261,9 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou } } } + if (mMessageContainer.getChildCount() == 0 && mIsolatedMessage != null) { + return mIsolatedMessage.getMeasuredType(); + } return MEASURED_NORMAL; } @@ -234,6 +276,7 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou result += ((MessagingLinearLayout.MessagingChild) child).getConsumedLines(); } } + result = mIsolatedMessage != null ? Math.max(result, 1) : result; // A group is usually taking up quite some space with the padding and the name, let's add 1 return result + 1; } @@ -289,26 +332,67 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou public void setMessages(List<MessagingMessage> group) { // Let's now make sure all children are added and in the correct order + int textMessageIndex = 0; + MessagingImageMessage isolatedMessage = null; for (int messageIndex = 0; messageIndex < group.size(); messageIndex++) { MessagingMessage message = group.get(messageIndex); + message.setColor(mTextColor); if (message.getGroup() != this) { message.setMessagingGroup(this); - ViewParent parent = mMessageContainer.getParent(); - if (parent instanceof ViewGroup) { - ((ViewGroup) parent).removeView(message); - } - mMessageContainer.addView(message, messageIndex); mAddedMessages.add(message); } - if (messageIndex != mMessageContainer.indexOfChild(message)) { - mMessageContainer.removeView(message); - mMessageContainer.addView(message, messageIndex); + boolean isImage = message instanceof MessagingImageMessage; + if (mAvatarsAtEnd && isImage) { + isolatedMessage = (MessagingImageMessage) message; + } else { + if (removeFromParentIfDifferent(message, mMessageContainer)) { + ViewGroup.LayoutParams layoutParams = message.getView().getLayoutParams(); + if (layoutParams != null + && !(layoutParams instanceof MessagingLinearLayout.LayoutParams)) { + message.getView().setLayoutParams( + mMessageContainer.generateDefaultLayoutParams()); + } + mMessageContainer.addView(message.getView(), textMessageIndex); + } + if (isImage) { + ((MessagingImageMessage) message).setIsolated(false); + } + // Let's sort them properly + if (textMessageIndex != mMessageContainer.indexOfChild(message.getView())) { + mMessageContainer.removeView(message.getView()); + mMessageContainer.addView(message.getView(), textMessageIndex); + } + textMessageIndex++; + } + } + if (isolatedMessage != null) { + if (removeFromParentIfDifferent(isolatedMessage, mImageContainer)) { + mImageContainer.removeAllViews(); + mImageContainer.addView(isolatedMessage.getView()); } - message.setTextColor(mTextColor); + isolatedMessage.setIsolated(true); + } else if (mIsolatedMessage != null) { + mImageContainer.removeAllViews(); } + mIsolatedMessage = isolatedMessage; mMessages = group; } + /** + * Remove the message from the parent if the parent isn't the one provided + * @return whether the message was removed + */ + private boolean removeFromParentIfDifferent(MessagingMessage message, ViewGroup newParent) { + ViewParent parent = message.getView().getParent(); + if (parent != newParent) { + if (parent instanceof ViewGroup) { + ((ViewGroup) parent).removeView(message.getView()); + } + return true; + } + return false; + } + @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); @@ -317,13 +401,14 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou @Override public boolean onPreDraw() { for (MessagingMessage message : mAddedMessages) { - if (!message.isShown()) { + if (!message.getView().isShown()) { continue; } - MessagingPropertyAnimator.fadeIn(message); + MessagingPropertyAnimator.fadeIn(message.getView()); if (!mFirstLayout) { - MessagingPropertyAnimator.startLocalTranslationFrom(message, - message.getHeight(), MessagingLayout.LINEAR_OUT_SLOW_IN); + MessagingPropertyAnimator.startLocalTranslationFrom(message.getView(), + message.getView().getHeight(), + MessagingLayout.LINEAR_OUT_SLOW_IN); } } mAddedMessages.clear(); @@ -333,6 +418,7 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou }); } mFirstLayout = false; + updateClipRect(); } /** @@ -372,6 +458,10 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou return mMessageContainer; } + public MessagingImageMessage getIsolatedMessage() { + return mIsolatedMessage; + } + public boolean needsGeneratedAvatar() { return mNeedsGeneratedAvatar; } @@ -379,4 +469,19 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou public Notification.Person getSender() { return mSender; } + + public void setTransformingImages(boolean transformingImages) { + mTransformingImages = transformingImages; + } + + public void setDisplayAvatarsAtEnd(boolean atEnd) { + if (mAvatarsAtEnd != atEnd) { + mAvatarsAtEnd = atEnd; + mImageContainer.setVisibility(atEnd ? View.VISIBLE : View.GONE); + } + } + + public List<MessagingMessage> getMessages() { + return mMessages; + } } diff --git a/core/java/com/android/internal/widget/MessagingImageMessage.java b/core/java/com/android/internal/widget/MessagingImageMessage.java new file mode 100644 index 000000000000..961f90a3c110 --- /dev/null +++ b/core/java/com/android/internal/widget/MessagingImageMessage.java @@ -0,0 +1,273 @@ +/* + * 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 com.android.internal.widget; + +import android.annotation.AttrRes; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.StyleRes; +import android.app.Notification; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Path; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.util.Log; +import android.util.Pools; +import android.view.LayoutInflater; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.RemoteViews; + +import com.android.internal.R; + +import java.io.IOException; + +/** + * A message of a {@link MessagingLayout} that is an image. + */ +@RemoteViews.RemoteView +public class MessagingImageMessage extends ImageView implements MessagingMessage { + private static final String TAG = "MessagingImageMessage"; + private static Pools.SimplePool<MessagingImageMessage> sInstancePool + = new Pools.SynchronizedPool<>(10); + private final MessagingMessageState mState = new MessagingMessageState(this); + private final int mMinImageHeight; + private final Path mPath = new Path(); + private final int mImageRounding; + private final int mMaxImageHeight; + private final int mIsolatedSize; + private final int mExtraSpacing; + private Drawable mDrawable; + private float mAspectRatio; + private int mActualWidth; + private int mActualHeight; + private boolean mIsIsolated; + + public MessagingImageMessage(@NonNull Context context) { + this(context, null); + } + + public MessagingImageMessage(@NonNull Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public MessagingImageMessage(@NonNull Context context, @Nullable AttributeSet attrs, + @AttrRes int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public MessagingImageMessage(@NonNull Context context, @Nullable AttributeSet attrs, + @AttrRes int defStyleAttr, @StyleRes int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + mMinImageHeight = context.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.messaging_image_min_size); + mMaxImageHeight = context.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.messaging_image_max_height); + mImageRounding = context.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.messaging_image_rounding); + mExtraSpacing = context.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.messaging_image_extra_spacing); + setMaxHeight(mMaxImageHeight); + mIsolatedSize = getResources().getDimensionPixelSize(R.dimen.messaging_avatar_size); + } + + @Override + public MessagingMessageState getState() { + return mState; + } + + @Override + public boolean setMessage(Notification.MessagingStyle.Message message) { + MessagingMessage.super.setMessage(message); + Drawable drawable; + try { + drawable = LocalImageResolver.resolveImage(message.getDataUri(), getContext()); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + int intrinsicHeight = drawable.getIntrinsicHeight(); + if (intrinsicHeight == 0) { + Log.w(TAG, "Drawable with 0 intrinsic height was returned"); + return false; + } + mDrawable = drawable; + mAspectRatio = ((float) mDrawable.getIntrinsicWidth()) / intrinsicHeight; + setImageDrawable(drawable); + setContentDescription(message.getText()); + return true; + } + + static MessagingMessage createMessage(MessagingLayout layout, + Notification.MessagingStyle.Message m) { + MessagingLinearLayout messagingLinearLayout = layout.getMessagingLinearLayout(); + MessagingImageMessage createdMessage = sInstancePool.acquire(); + if (createdMessage == null) { + createdMessage = (MessagingImageMessage) LayoutInflater.from( + layout.getContext()).inflate( + R.layout.notification_template_messaging_image_message, + messagingLinearLayout, + false); + createdMessage.addOnLayoutChangeListener(MessagingLayout.MESSAGING_PROPERTY_ANIMATOR); + } + boolean created = createdMessage.setMessage(m); + if (!created) { + createdMessage.recycle(); + return MessagingTextMessage.createMessage(layout, m); + } + return createdMessage; + } + + @Override + protected void onDraw(Canvas canvas) { + canvas.save(); + canvas.clipPath(getRoundedRectPath()); + int width = (int) Math.max(getActualWidth(), getActualHeight() * mAspectRatio); + int height = (int) (width / mAspectRatio); + int left = (int) ((getActualWidth() - width) / 2.0f); + mDrawable.setBounds(left, 0, left + width, height); + mDrawable.draw(canvas); + canvas.restore(); + } + + public Path getRoundedRectPath() { + int left = 0; + int right = getActualWidth(); + int top = 0; + int bottom = getActualHeight(); + mPath.reset(); + int width = right - left; + float roundnessX = mImageRounding; + float roundnessY = mImageRounding; + roundnessX = Math.min(width / 2, roundnessX); + roundnessY = Math.min((bottom - top) / 2, roundnessY); + mPath.moveTo(left, top + roundnessY); + mPath.quadTo(left, top, left + roundnessX, top); + mPath.lineTo(right - roundnessX, top); + mPath.quadTo(right, top, right, top + roundnessY); + mPath.lineTo(right, bottom - roundnessY); + mPath.quadTo(right, bottom, right - roundnessX, bottom); + mPath.lineTo(left + roundnessX, bottom); + mPath.quadTo(left, bottom, left, bottom - roundnessY); + mPath.close(); + return mPath; + } + + public void recycle() { + MessagingMessage.super.recycle(); + setAlpha(1.0f); + setTranslationY(0); + setImageBitmap(null); + mDrawable = null; + sInstancePool.release(this); + } + + public static void dropCache() { + sInstancePool = new Pools.SynchronizedPool<>(10); + } + + @Override + public int getMeasuredType() { + int measuredHeight = getMeasuredHeight(); + int minImageHeight; + if (mIsIsolated) { + minImageHeight = mIsolatedSize; + } else { + minImageHeight = mMinImageHeight; + } + boolean measuredTooSmall = measuredHeight < minImageHeight + && measuredHeight != mDrawable.getIntrinsicHeight(); + if (measuredTooSmall) { + return MEASURED_TOO_SMALL; + } else { + if (!mIsIsolated && measuredHeight != mDrawable.getIntrinsicHeight()) { + return MEASURED_SHORTENED; + } else { + return MEASURED_NORMAL; + } + } + } + + @Override + public void setMaxDisplayedLines(int lines) { + // Nothing to do, this should be handled automatically. + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + if (mIsIsolated) { + setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), + MeasureSpec.getSize(heightMeasureSpec)); + } + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + // TODO: ensure that this isn't called when transforming + setActualWidth(getStaticWidth()); + setActualHeight(getHeight()); + } + + @Override + public int getConsumedLines() { + return 3; + } + + public void setActualWidth(int actualWidth) { + mActualWidth = actualWidth; + invalidate(); + } + + public int getActualWidth() { + return mActualWidth; + } + + public void setActualHeight(int actualHeight) { + mActualHeight = actualHeight; + invalidate(); + } + + public int getActualHeight() { + return mActualHeight; + } + + public int getStaticWidth() { + if (mIsIsolated) { + return getWidth(); + } + return (int) (getHeight() * mAspectRatio); + } + + public void setIsolated(boolean isolated) { + if (mIsIsolated != isolated) { + mIsIsolated = isolated; + // update the layout params not to have margins + ViewGroup.MarginLayoutParams layoutParams = + (ViewGroup.MarginLayoutParams) getLayoutParams(); + layoutParams.topMargin = isolated ? 0 : mExtraSpacing; + setLayoutParams(layoutParams); + } + } + + @Override + public int getExtraSpacing() { + return mExtraSpacing; + } +} diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java index d45c086e3b52..5279636ae2a0 100644 --- a/core/java/com/android/internal/widget/MessagingLayout.java +++ b/core/java/com/android/internal/widget/MessagingLayout.java @@ -81,6 +81,7 @@ public class MessagingLayout extends FrameLayout { private ArrayList<MessagingGroup> mAddedGroups = new ArrayList<>(); private Notification.Person mUser; private CharSequence mNameReplacement; + private boolean mIsCollapsed; public MessagingLayout(@NonNull Context context) { super(context); @@ -127,6 +128,11 @@ public class MessagingLayout extends FrameLayout { } @RemotableViewMethod + public void setIsCollapsed(boolean isCollapsed) { + mIsCollapsed = isCollapsed; + } + + @RemotableViewMethod public void setData(Bundle extras) { Parcelable[] messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES); List<Notification.MessagingStyle.Message> newMessages @@ -331,6 +337,7 @@ public class MessagingLayout extends FrameLayout { newGroup = MessagingGroup.createGroup(mMessagingLinearLayout); mAddedGroups.add(newGroup); } + newGroup.setDisplayAvatarsAtEnd(mIsCollapsed); newGroup.setLayoutColor(mLayoutColor); Notification.Person sender = senders.get(groupIndex); CharSequence nameOverride = null; @@ -392,7 +399,6 @@ public class MessagingLayout extends FrameLayout { MessagingMessage message = findAndRemoveMatchingMessage(m); if (message == null) { message = MessagingMessage.createMessage(this, m); - message.addOnLayoutChangeListener(MESSAGING_PROPERTY_ANIMATOR); } message.setIsHistoric(historic); result.add(message); diff --git a/core/java/com/android/internal/widget/MessagingLinearLayout.java b/core/java/com/android/internal/widget/MessagingLinearLayout.java index f0ef37076618..991e3e7a80f9 100644 --- a/core/java/com/android/internal/widget/MessagingLinearLayout.java +++ b/core/java/com/android/internal/widget/MessagingLinearLayout.java @@ -75,7 +75,6 @@ public class MessagingLinearLayout extends ViewGroup { targetHeight = Integer.MAX_VALUE; break; } - int widthSize = MeasureSpec.getSize(widthMeasureSpec); // Now that we know which views to take, fix up the indents and see what width we get. int measuredWidth = mPaddingLeft + mPaddingRight; @@ -90,7 +89,6 @@ public class MessagingLinearLayout extends ViewGroup { totalHeight = mPaddingTop + mPaddingBottom; boolean first = true; int linesRemaining = mMaxDisplayedLines; - // Starting from the bottom: we measure every view as if it were the only one. If it still // fits, we take it, otherwise we stop there. for (int i = count - 1; i >= 0 && totalHeight < targetHeight; i--) { @@ -100,11 +98,13 @@ public class MessagingLinearLayout extends ViewGroup { final View child = getChildAt(i); LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams(); MessagingChild messagingChild = null; + int spacing = mSpacing; if (child instanceof MessagingChild) { messagingChild = (MessagingChild) child; messagingChild.setMaxDisplayedLines(linesRemaining); + spacing += messagingChild.getExtraSpacing(); } - int spacing = first ? 0 : mSpacing; + spacing = first ? 0 : spacing; measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, totalHeight - mPaddingTop - mPaddingBottom + spacing); @@ -254,6 +254,9 @@ public class MessagingLinearLayout extends ViewGroup { void setMaxDisplayedLines(int lines); void hideAnimated(); boolean isHidingAnimated(); + default int getExtraSpacing() { + return 0; + } } public static class LayoutParams extends MarginLayoutParams { diff --git a/core/java/com/android/internal/widget/MessagingMessage.java b/core/java/com/android/internal/widget/MessagingMessage.java index f09621f544bc..bf1c5ca747a4 100644 --- a/core/java/com/android/internal/widget/MessagingMessage.java +++ b/core/java/com/android/internal/widget/MessagingMessage.java @@ -16,182 +16,125 @@ package com.android.internal.widget; -import android.annotation.AttrRes; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.StyleRes; import android.app.Notification; -import android.content.Context; -import android.text.Layout; -import android.util.AttributeSet; -import android.util.Pools; -import android.view.LayoutInflater; -import android.view.ViewGroup; -import android.view.ViewParent; -import android.widget.RemoteViews; - -import com.android.internal.R; +import android.view.View; import java.util.Objects; /** * A message of a {@link MessagingLayout}. */ -@RemoteViews.RemoteView -public class MessagingMessage extends ImageFloatingTextView implements - MessagingLinearLayout.MessagingChild { - - private static Pools.SimplePool<MessagingMessage> sInstancePool - = new Pools.SynchronizedPool<>(10); - private Notification.MessagingStyle.Message mMessage; - private MessagingGroup mGroup; - private boolean mIsHistoric; - private boolean mIsHidingAnimated; +public interface MessagingMessage extends MessagingLinearLayout.MessagingChild { - public MessagingMessage(@NonNull Context context) { - super(context); - } + /** + * Prefix for supported image MIME types + **/ + String IMAGE_MIME_TYPE_PREFIX = "image/"; - public MessagingMessage(@NonNull Context context, @Nullable AttributeSet attrs) { - super(context, attrs); + static MessagingMessage createMessage(MessagingLayout layout, + Notification.MessagingStyle.Message m) { + if (hasImage(m)) { + return MessagingImageMessage.createMessage(layout, m); + } else { + return MessagingTextMessage.createMessage(layout, m); + } } - public MessagingMessage(@NonNull Context context, @Nullable AttributeSet attrs, - @AttrRes int defStyleAttr) { - super(context, attrs, defStyleAttr); + static void dropCache() { + MessagingTextMessage.dropCache(); + MessagingImageMessage.dropCache(); } - public MessagingMessage(@NonNull Context context, @Nullable AttributeSet attrs, - @AttrRes int defStyleAttr, @StyleRes int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); + static boolean hasImage(Notification.MessagingStyle.Message m) { + return m.getDataUri() != null + && m.getDataMimeType() != null + && m.getDataMimeType().startsWith(IMAGE_MIME_TYPE_PREFIX); } - private void setMessage(Notification.MessagingStyle.Message message) { - mMessage = message; - setText(message.getText()); + /** + * Set a message for this view. + * @return true if setting the message worked + */ + default boolean setMessage(Notification.MessagingStyle.Message message) { + getState().setMessage(message); + return true; } - public Notification.MessagingStyle.Message getMessage() { - return mMessage; + default Notification.MessagingStyle.Message getMessage() { + return getState().getMessage(); } - boolean sameAs(Notification.MessagingStyle.Message message) { - if (!Objects.equals(message.getText(), mMessage.getText())) { + default boolean sameAs(Notification.MessagingStyle.Message message) { + Notification.MessagingStyle.Message ownMessage = getMessage(); + if (!Objects.equals(message.getText(), ownMessage.getText())) { return false; } - if (!Objects.equals(message.getSender(), mMessage.getSender())) { + if (!Objects.equals(message.getSender(), ownMessage.getSender())) { return false; } - if (!Objects.equals(message.getTimestamp(), mMessage.getTimestamp())) { + if (!Objects.equals(message.getTimestamp(), ownMessage.getTimestamp())) { + return false; + } + if (!Objects.equals(message.getDataMimeType(), ownMessage.getDataMimeType())) { + return false; + } + if (!Objects.equals(message.getDataUri(), ownMessage.getDataUri())) { return false; } return true; } - boolean sameAs(MessagingMessage message) { + default boolean sameAs(MessagingMessage message) { return sameAs(message.getMessage()); } - static MessagingMessage createMessage(MessagingLayout layout, - Notification.MessagingStyle.Message m) { - MessagingLinearLayout messagingLinearLayout = layout.getMessagingLinearLayout(); - MessagingMessage createdMessage = sInstancePool.acquire(); - if (createdMessage == null) { - createdMessage = (MessagingMessage) LayoutInflater.from(layout.getContext()).inflate( - R.layout.notification_template_messaging_message, messagingLinearLayout, - false); - } - createdMessage.setMessage(m); - return createdMessage; - } - - public void removeMessage() { - mGroup.removeMessage(this); - } - - public void recycle() { - mGroup = null; - mMessage = null; - setAlpha(1.0f); - setTranslationY(0); - sInstancePool.release(this); + default void removeMessage() { + getGroup().removeMessage(this); } - public void setMessagingGroup(MessagingGroup group) { - mGroup = group; + default void setMessagingGroup(MessagingGroup group) { + getState().setGroup(group); } - public static void dropCache() { - sInstancePool = new Pools.SynchronizedPool<>(10); + default void setIsHistoric(boolean isHistoric) { + getState().setIsHistoric(isHistoric); } - public void setIsHistoric(boolean isHistoric) { - mIsHistoric = isHistoric; + default MessagingGroup getGroup() { + return getState().getGroup(); } - public MessagingGroup getGroup() { - return mGroup; + default void setIsHidingAnimated(boolean isHiding) { + getState().setIsHidingAnimated(isHiding); } @Override - public int getMeasuredType() { - boolean measuredTooSmall = getMeasuredHeight() - < getLayoutHeight() + getPaddingTop() + getPaddingBottom(); - if (measuredTooSmall) { - return MEASURED_TOO_SMALL; - } else { - Layout layout = getLayout(); - if (layout == null) { - return MEASURED_TOO_SMALL; - } - if (layout.getEllipsisCount(layout.getLineCount() - 1) > 0) { - return MEASURED_SHORTENED; - } else { - return MEASURED_NORMAL; - } - } + default boolean isHidingAnimated() { + return getState().isHidingAnimated(); } @Override - public void hideAnimated() { + default void hideAnimated() { setIsHidingAnimated(true); - mGroup.performRemoveAnimation(this, () -> setIsHidingAnimated(false)); + getGroup().performRemoveAnimation(getState().getHostView(), + () -> setIsHidingAnimated(false)); } - private void setIsHidingAnimated(boolean isHiding) { - ViewParent parent = getParent(); - mIsHidingAnimated = isHiding; - invalidate(); - if (parent instanceof ViewGroup) { - ((ViewGroup) parent).invalidate(); - } + default boolean hasOverlappingRendering() { + return false; } - @Override - public boolean isHidingAnimated() { - return mIsHidingAnimated; + default void recycle() { + getState().reset(); } - @Override - public void setMaxDisplayedLines(int lines) { - setMaxLines(lines); + default View getView() { + return (View) this; } - @Override - public int getConsumedLines() { - return getLineCount(); - } + default void setColor(int textColor) {} - public int getLayoutHeight() { - Layout layout = getLayout(); - if (layout == null) { - return 0; - } - return layout.getHeight(); - } + MessagingMessageState getState(); - @Override - public boolean hasOverlappingRendering() { - return false; - } + void setVisibility(int visibility); } diff --git a/core/java/com/android/internal/widget/MessagingMessageState.java b/core/java/com/android/internal/widget/MessagingMessageState.java new file mode 100644 index 000000000000..ac624728689d --- /dev/null +++ b/core/java/com/android/internal/widget/MessagingMessageState.java @@ -0,0 +1,81 @@ +/* + * 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 com.android.internal.widget; + +import android.app.Notification; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewParent; + +/** + * Shared state and implementation for MessagingMessages. Used to share common implementations. + */ +public class MessagingMessageState { + private final View mHostView; + private Notification.MessagingStyle.Message mMessage; + private MessagingGroup mGroup; + private boolean mIsHistoric; + private boolean mIsHidingAnimated; + + MessagingMessageState(View hostView) { + mHostView = hostView; + } + + public void setMessage(Notification.MessagingStyle.Message message) { + mMessage = message; + } + + public Notification.MessagingStyle.Message getMessage() { + return mMessage; + } + + public void setGroup(MessagingGroup group) { + mGroup = group; + } + + public MessagingGroup getGroup() { + return mGroup; + } + + public void setIsHistoric(boolean isHistoric) { + mIsHistoric = isHistoric; + } + + public void setIsHidingAnimated(boolean isHiding) { + ViewParent parent = mHostView.getParent(); + mIsHidingAnimated = isHiding; + mHostView.invalidate(); + if (parent instanceof ViewGroup) { + ((ViewGroup) parent).invalidate(); + } + } + + public boolean isHidingAnimated() { + return mIsHidingAnimated; + } + + public View getHostView() { + return mHostView; + } + + public void reset() { + mIsHidingAnimated = false; + mIsHistoric = false; + mGroup = null; + mMessage = null; + } +} diff --git a/core/java/com/android/internal/widget/MessagingTextMessage.java b/core/java/com/android/internal/widget/MessagingTextMessage.java new file mode 100644 index 000000000000..794cc1dc66f7 --- /dev/null +++ b/core/java/com/android/internal/widget/MessagingTextMessage.java @@ -0,0 +1,145 @@ +/* + * 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 com.android.internal.widget; + +import android.annotation.AttrRes; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.StyleRes; +import android.app.Notification; +import android.content.Context; +import android.text.Layout; +import android.util.AttributeSet; +import android.util.Pools; +import android.view.LayoutInflater; +import android.view.ViewGroup; +import android.view.ViewParent; +import android.widget.RemoteViews; + +import com.android.internal.R; + +import java.util.Objects; + +/** + * A message of a {@link MessagingLayout}. + */ +@RemoteViews.RemoteView +public class MessagingTextMessage extends ImageFloatingTextView implements MessagingMessage { + + private static Pools.SimplePool<MessagingTextMessage> sInstancePool + = new Pools.SynchronizedPool<>(20); + private final MessagingMessageState mState = new MessagingMessageState(this); + + public MessagingTextMessage(@NonNull Context context) { + super(context); + } + + public MessagingTextMessage(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public MessagingTextMessage(@NonNull Context context, @Nullable AttributeSet attrs, + @AttrRes int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public MessagingTextMessage(@NonNull Context context, @Nullable AttributeSet attrs, + @AttrRes int defStyleAttr, @StyleRes int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + public MessagingMessageState getState() { + return mState; + } + + @Override + public boolean setMessage(Notification.MessagingStyle.Message message) { + MessagingMessage.super.setMessage(message); + setText(message.getText()); + return true; + } + + static MessagingMessage createMessage(MessagingLayout layout, + Notification.MessagingStyle.Message m) { + MessagingLinearLayout messagingLinearLayout = layout.getMessagingLinearLayout(); + MessagingTextMessage createdMessage = sInstancePool.acquire(); + if (createdMessage == null) { + createdMessage = (MessagingTextMessage) LayoutInflater.from( + layout.getContext()).inflate( + R.layout.notification_template_messaging_text_message, + messagingLinearLayout, + false); + createdMessage.addOnLayoutChangeListener(MessagingLayout.MESSAGING_PROPERTY_ANIMATOR); + } + createdMessage.setMessage(m); + return createdMessage; + } + + public void recycle() { + MessagingMessage.super.recycle(); + setAlpha(1.0f); + setTranslationY(0); + sInstancePool.release(this); + } + + public static void dropCache() { + sInstancePool = new Pools.SynchronizedPool<>(10); + } + + @Override + public int getMeasuredType() { + boolean measuredTooSmall = getMeasuredHeight() + < getLayoutHeight() + getPaddingTop() + getPaddingBottom(); + if (measuredTooSmall) { + return MEASURED_TOO_SMALL; + } else { + Layout layout = getLayout(); + if (layout == null) { + return MEASURED_TOO_SMALL; + } + if (layout.getEllipsisCount(layout.getLineCount() - 1) > 0) { + return MEASURED_SHORTENED; + } else { + return MEASURED_NORMAL; + } + } + } + + @Override + public void setMaxDisplayedLines(int lines) { + setMaxLines(lines); + } + + @Override + public int getConsumedLines() { + return getLineCount(); + } + + public int getLayoutHeight() { + Layout layout = getLayout(); + if (layout == null) { + return 0; + } + return layout.getHeight(); + } + + @Override + public void setColor(int color) { + setTextColor(color); + } +} diff --git a/core/java/com/android/internal/widget/VerifyCredentialResponse.java b/core/java/com/android/internal/widget/VerifyCredentialResponse.java index ad6020c0846c..7d1c70647092 100644 --- a/core/java/com/android/internal/widget/VerifyCredentialResponse.java +++ b/core/java/com/android/internal/widget/VerifyCredentialResponse.java @@ -98,6 +98,8 @@ public final class VerifyCredentialResponse implements Parcelable { if (mPayload != null) { dest.writeInt(mPayload.length); dest.writeByteArray(mPayload); + } else { + dest.writeInt(0); } } } diff --git a/core/java/com/android/internal/widget/ViewPager.java b/core/java/com/android/internal/widget/ViewPager.java index d5b6def97426..64bdc6e1eb37 100644 --- a/core/java/com/android/internal/widget/ViewPager.java +++ b/core/java/com/android/internal/widget/ViewPager.java @@ -31,6 +31,7 @@ import android.os.Parcelable; import android.util.AttributeSet; import android.util.Log; import android.util.MathUtils; +import android.view.AbsSavedState; import android.view.FocusFinder; import android.view.Gravity; import android.view.KeyEvent; @@ -1198,16 +1199,12 @@ public class ViewPager extends ViewGroup { * state, in which case it should implement a subclass of this which * contains that state. */ - public static class SavedState extends BaseSavedState { + public static class SavedState extends AbsSavedState { int position; Parcelable adapterState; ClassLoader loader; - public SavedState(Parcel source) { - super(source); - } - - public SavedState(Parcelable superState) { + public SavedState(@NonNull Parcelable superState) { super(superState); } @@ -1225,10 +1222,15 @@ public class ViewPager extends ViewGroup { + " position=" + position + "}"; } - public static final Creator<SavedState> CREATOR = new Creator<SavedState>() { + public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() { + @Override + public SavedState createFromParcel(Parcel in, ClassLoader loader) { + return new SavedState(in, loader); + } + @Override public SavedState createFromParcel(Parcel in) { - return new SavedState(in); + return new SavedState(in, null); } @Override public SavedState[] newArray(int size) { @@ -1237,7 +1239,7 @@ public class ViewPager extends ViewGroup { }; SavedState(Parcel in, ClassLoader loader) { - super(in); + super(in, loader); if (loader == null) { loader = getClass().getClassLoader(); } diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java index fb186693979d..95534e264b80 100644 --- a/core/java/com/android/server/BootReceiver.java +++ b/core/java/com/android/server/BootReceiver.java @@ -72,6 +72,7 @@ public class BootReceiver extends BroadcastReceiver { SystemProperties.getInt("ro.debuggable", 0) == 1 ? 98304 : 65536; private static final File TOMBSTONE_DIR = new File("/data/tombstones"); + private static final String TAG_TOMBSTONE = "SYSTEM_TOMBSTONE"; // The pre-froyo package and class of the system updater, which // ran in the system process. We need to remove its packages here @@ -164,7 +165,7 @@ public class BootReceiver extends BroadcastReceiver { .append("Revision: ") .append(SystemProperties.get("ro.revision", "")).append("\n") .append("Bootloader: ").append(Build.BOOTLOADER).append("\n") - .append("Radio: ").append(Build.RADIO).append("\n") + .append("Radio: ").append(Build.getRadioVersion()).append("\n") .append("Kernel: ") .append(FileUtils.readTextFile(new File("/proc/version"), 1024, "...\n")) .append("\n").toString(); @@ -265,7 +266,7 @@ public class BootReceiver extends BroadcastReceiver { File file = new File(TOMBSTONE_DIR, path); if (file.isFile()) { addFileToDropBox(db, timestamps, headers, file.getPath(), LOG_SIZE, - "SYSTEM_TOMBSTONE"); + TAG_TOMBSTONE); } } catch (IOException e) { Slog.e(TAG, "Can't log tombstone", e); @@ -299,9 +300,20 @@ public class BootReceiver extends BroadcastReceiver { timestamps.put(filename, fileTime); + + String fileContents = FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n"); + String text = headers + fileContents + footers; + // Create an additional report for system server native crashes, with a special tag. + if (tag.equals(TAG_TOMBSTONE) && fileContents.contains(">>> system_server <<<")) { + addTextToDropBox(db, "system_server_native_crash", text, filename, maxSize); + } + addTextToDropBox(db, tag, text, filename, maxSize); + } + + private static void addTextToDropBox(DropBoxManager db, String tag, String text, + String filename, int maxSize) { Slog.i(TAG, "Copying " + filename + " to DropBox (" + tag + ")"); - db.addText(tag, headers + FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n") + - footers); + db.addText(tag, text); EventLog.writeEvent(DropboxLogTags.DROPBOX_FILE_COPY, filename, maxSize, tag); } |