Merge "Add tests for Watchdog logging metrics"
diff --git a/apct-tests/perftests/blobstore/Android.bp b/apct-tests/perftests/blobstore/Android.bp
new file mode 100644
index 0000000..be5072c
--- /dev/null
+++ b/apct-tests/perftests/blobstore/Android.bp
@@ -0,0 +1,28 @@
+// Copyright (C) 2020 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.
+
+android_test {
+  name: "BlobStorePerfTests",
+  srcs: ["src/**/*.java"],
+  static_libs: [
+    "BlobStoreTestUtils",
+    "androidx.test.rules",
+    "androidx.annotation_annotation",
+    "apct-perftests-utils",
+    "ub-uiautomator",
+  ],
+  platform_apis: true,
+  test_suites: ["device-tests"],
+  certificate: "platform",
+}
\ No newline at end of file
diff --git a/apct-tests/perftests/blobstore/AndroidManifest.xml b/apct-tests/perftests/blobstore/AndroidManifest.xml
new file mode 100644
index 0000000..21d0726
--- /dev/null
+++ b/apct-tests/perftests/blobstore/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.perftests.blob">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.perftests.blob"/>
+
+</manifest>
\ No newline at end of file
diff --git a/apct-tests/perftests/blobstore/AndroidTest.xml b/apct-tests/perftests/blobstore/AndroidTest.xml
new file mode 100644
index 0000000..19456c6
--- /dev/null
+++ b/apct-tests/perftests/blobstore/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+<configuration description="Runs BlobStorePerfTests metric instrumentation.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-metric-instrumentation" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="BlobStorePerfTests.apk" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.perftests.blob" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/AtraceUtils.java b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/AtraceUtils.java
new file mode 100644
index 0000000..0208dab
--- /dev/null
+++ b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/AtraceUtils.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2020 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.perftests.blob;
+
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.os.ParcelFileDescriptor;
+import android.perftests.utils.TraceMarkParser;
+import android.perftests.utils.TraceMarkParser.TraceMarkSlice;
+import android.support.test.uiautomator.UiDevice;
+import android.util.Log;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.List;
+import java.util.function.BiConsumer;
+
+// Copy of com.android.frameworks.perftests.am.util.AtraceUtils. TODO: avoid this duplication.
+public class AtraceUtils {
+    private static final String TAG = "AtraceUtils";
+    private static final boolean VERBOSE = true;
+
+    private static final String ATRACE_START = "atrace --async_start -b %d -c %s";
+    private static final String ATRACE_DUMP = "atrace --async_dump";
+    private static final String ATRACE_STOP = "atrace --async_stop";
+    private static final int DEFAULT_ATRACE_BUF_SIZE = 1024;
+
+    private UiAutomation mAutomation;
+    private static AtraceUtils sUtils = null;
+    private boolean mStarted = false;
+
+    private AtraceUtils(Instrumentation instrumentation) {
+        mAutomation = instrumentation.getUiAutomation();
+    }
+
+    public static AtraceUtils getInstance(Instrumentation instrumentation) {
+        if (sUtils == null) {
+            sUtils = new AtraceUtils(instrumentation);
+        }
+        return sUtils;
+    }
+
+    /**
+     * @param categories The list of the categories to trace, separated with space.
+     */
+    public void startTrace(String categories) {
+        synchronized (this) {
+            if (mStarted) {
+                throw new IllegalStateException("atrace already started");
+            }
+            runShellCommand(String.format(
+                    ATRACE_START, DEFAULT_ATRACE_BUF_SIZE, categories));
+            mStarted = true;
+        }
+    }
+
+    public void stopTrace() {
+        synchronized (this) {
+            mStarted = false;
+            runShellCommand(ATRACE_STOP);
+        }
+    }
+
+    private String runShellCommand(String cmd) {
+        try {
+            return UiDevice.getInstance(
+                    InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * @param parser The function that can accept the buffer of atrace dump and parse it.
+     * @param handler The parse result handler
+     */
+    public void performDump(TraceMarkParser parser,
+            BiConsumer<String, List<TraceMarkSlice>> handler) {
+        parser.reset();
+        try {
+            if (VERBOSE) {
+                Log.i(TAG, "Collecting atrace dump...");
+            }
+            writeDataToBuf(mAutomation.executeShellCommand(ATRACE_DUMP), parser);
+        } catch (IOException e) {
+            Log.e(TAG, "Error in reading dump", e);
+        }
+        parser.forAllSlices(handler);
+    }
+
+    // The given file descriptor here will be closed by this function
+    private void writeDataToBuf(ParcelFileDescriptor pfDescriptor,
+            TraceMarkParser parser) throws IOException {
+        InputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(pfDescriptor);
+        try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
+            String line;
+            while ((line = reader.readLine()) != null) {
+                parser.visit(line);
+            }
+        }
+    }
+}
diff --git a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
new file mode 100644
index 0000000..a7b69c4
--- /dev/null
+++ b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2020 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.perftests.blob;
+
+import android.app.blob.BlobStoreManager;
+import android.content.Context;
+import android.perftests.utils.ManualBenchmarkState;
+import android.perftests.utils.PerfManualStatusReporter;
+import android.perftests.utils.TraceMarkParser;
+import android.perftests.utils.TraceMarkParser.TraceMarkSlice;
+import android.support.test.uiautomator.UiDevice;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.utils.blob.DummyBlobData;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
+@LargeTest
+@RunWith(Parameterized.class)
+public class BlobStorePerfTests {
+    // From frameworks/native/cmds/atrace/atrace.cpp
+    private static final String ATRACE_CATEGORY_SYSTEM_SERVER = "ss";
+    // From f/b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
+    private static final String ATRACE_COMPUTE_DIGEST_PREFIX = "computeBlobDigest-";
+
+    private Context mContext;
+    private BlobStoreManager mBlobStoreManager;
+    private AtraceUtils mAtraceUtils;
+    private ManualBenchmarkState mState;
+
+    @Rule
+    public PerfManualStatusReporter mPerfManualStatusReporter = new PerfManualStatusReporter();
+
+    @Parameterized.Parameter(0)
+    public int fileSizeInMb;
+
+    @Parameterized.Parameters(name = "{0}MB")
+    public static Collection<Object[]> getParameters() {
+        return Arrays.asList(new Object[][] {
+                { 25 },
+                { 50 },
+                { 100 },
+                { 200 },
+        });
+    }
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        mBlobStoreManager = (BlobStoreManager) mContext.getSystemService(
+                Context.BLOB_STORE_SERVICE);
+        mAtraceUtils = AtraceUtils.getInstance(InstrumentationRegistry.getInstrumentation());
+        mState = mPerfManualStatusReporter.getBenchmarkState();
+    }
+
+    @After
+    public void tearDown() {
+        // TODO: Add a blob_store shell command to trigger idle maintenance to avoid hardcoding
+        // job id like this.
+        // From BlobStoreConfig.IDLE_JOB_ID = 191934935.
+        runShellCommand("cmd jobscheduler run -f android 191934935");
+    }
+
+    @Test
+    public void testComputeDigest() throws Exception {
+        mAtraceUtils.startTrace(ATRACE_CATEGORY_SYSTEM_SERVER);
+        try {
+            final List<Long> durations = new ArrayList<>();
+            final DummyBlobData blobData = prepareDataBlob(fileSizeInMb);
+            final TraceMarkParser parser = new TraceMarkParser(
+                    line -> line.name.startsWith(ATRACE_COMPUTE_DIGEST_PREFIX));
+            while (mState.keepRunning(durations)) {
+                commitBlob(blobData);
+
+                durations.clear();
+                collectDigestDurationsFromTrace(parser, durations);
+                // get and delete blobId
+            }
+        } finally {
+            mAtraceUtils.stopTrace();
+        }
+    }
+
+    private void collectDigestDurationsFromTrace(TraceMarkParser parser, List<Long> durations) {
+        mAtraceUtils.performDump(parser, (key, slices) -> {
+            for (TraceMarkSlice slice : slices) {
+                durations.add(TimeUnit.MICROSECONDS.toNanos(slice.getDurationInMicroseconds()));
+            }
+        });
+    }
+
+    private DummyBlobData prepareDataBlob(int fileSizeInMb) throws Exception {
+        final DummyBlobData blobData = new DummyBlobData(mContext,
+                fileSizeInMb * 1024 * 1024 /* bytes */);
+        blobData.prepare();
+        return blobData;
+    }
+
+    private void commitBlob(DummyBlobData blobData) throws Exception {
+        final long sessionId = mBlobStoreManager.createSession(blobData.getBlobHandle());
+        try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
+            blobData.writeToSession(session);
+            final CompletableFuture<Integer> callback = new CompletableFuture<>();
+            session.commit(mContext.getMainExecutor(), callback::complete);
+            // Ignore commit callback result.
+            callback.get();
+        }
+    }
+
+    private String runShellCommand(String cmd) {
+        try {
+            return UiDevice.getInstance(
+                    InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/apex/blobstore/TEST_MAPPING b/apex/blobstore/TEST_MAPPING
index cfe19a5..25a1537 100644
--- a/apex/blobstore/TEST_MAPPING
+++ b/apex/blobstore/TEST_MAPPING
@@ -4,7 +4,7 @@
       "name": "CtsBlobStoreTestCases"
     },
     {
-      "name": "FrameworksServicesTests",
+      "name": "FrameworksMockingServicesTests",
       "options": [
         {
           "include-filter": "com.android.server.blob"
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobHandle.java b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
index f110b36..d339afa 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
@@ -257,6 +257,11 @@
         return Base64.encodeToString(digest, Base64.NO_WRAP);
     }
 
+    /** @hide */
+    public boolean isExpired() {
+        return expiryTimeMillis != 0 && expiryTimeMillis < System.currentTimeMillis();
+    }
+
     public static final @NonNull Creator<BlobHandle> CREATOR = new Creator<BlobHandle>() {
         @Override
         public @NonNull BlobHandle createFromParcel(@NonNull Parcel source) {
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
index aba3e8c..c12e0ec 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
@@ -64,9 +64,9 @@
 
     private final Context mContext;
 
-    public final long blobId;
-    public final BlobHandle blobHandle;
-    public final int userId;
+    private final long mBlobId;
+    private final BlobHandle mBlobHandle;
+    private final int mUserId;
 
     @GuardedBy("mMetadataLock")
     private final ArraySet<Committer> mCommitters = new ArraySet<>();
@@ -90,9 +90,21 @@
 
     BlobMetadata(Context context, long blobId, BlobHandle blobHandle, int userId) {
         mContext = context;
-        this.blobId = blobId;
-        this.blobHandle = blobHandle;
-        this.userId = userId;
+        this.mBlobId = blobId;
+        this.mBlobHandle = blobHandle;
+        this.mUserId = userId;
+    }
+
+    long getBlobId() {
+        return mBlobId;
+    }
+
+    BlobHandle getBlobHandle() {
+        return mBlobHandle;
+    }
+
+    int getUserId() {
+        return mUserId;
     }
 
     void addCommitter(@NonNull Committer committer) {
@@ -159,7 +171,7 @@
 
     boolean hasLeases() {
         synchronized (mMetadataLock) {
-            return mLeasees.isEmpty();
+            return !mLeasees.isEmpty();
         }
     }
 
@@ -196,7 +208,7 @@
 
     File getBlobFile() {
         if (mBlobFile == null) {
-            mBlobFile = BlobStoreConfig.getBlobFile(blobId);
+            mBlobFile = BlobStoreConfig.getBlobFile(mBlobId);
         }
         return mBlobFile;
     }
@@ -244,7 +256,7 @@
     void dump(IndentingPrintWriter fout, DumpArgs dumpArgs) {
         fout.println("blobHandle:");
         fout.increaseIndent();
-        blobHandle.dump(fout, dumpArgs.shouldDumpFull());
+        mBlobHandle.dump(fout, dumpArgs.shouldDumpFull());
         fout.decreaseIndent();
 
         fout.println("Committers:");
@@ -274,11 +286,11 @@
 
     void writeToXml(XmlSerializer out) throws IOException {
         synchronized (mMetadataLock) {
-            XmlUtils.writeLongAttribute(out, ATTR_ID, blobId);
-            XmlUtils.writeIntAttribute(out, ATTR_USER_ID, userId);
+            XmlUtils.writeLongAttribute(out, ATTR_ID, mBlobId);
+            XmlUtils.writeIntAttribute(out, ATTR_USER_ID, mUserId);
 
             out.startTag(null, TAG_BLOB_HANDLE);
-            blobHandle.writeToXml(out);
+            mBlobHandle.writeToXml(out);
             out.endTag(null, TAG_BLOB_HANDLE);
 
             for (int i = 0, count = mCommitters.size(); i < count; ++i) {
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
index eb414b0..ba2e559 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
@@ -18,12 +18,15 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Environment;
+import android.util.Log;
 import android.util.Slog;
 
 import java.io.File;
+import java.util.concurrent.TimeUnit;
 
 class BlobStoreConfig {
     public static final String TAG = "BlobStore";
+    public static final boolean LOGV = Log.isLoggable(TAG, Log.VERBOSE);
 
     public static final int CURRENT_XML_VERSION = 1;
 
@@ -32,6 +35,20 @@
     private static final String SESSIONS_INDEX_FILE_NAME = "sessions_index.xml";
     private static final String BLOBS_INDEX_FILE_NAME = "blobs_index.xml";
 
+    /**
+     * Job Id for idle maintenance job ({@link BlobStoreIdleJobService}).
+     */
+    public static final int IDLE_JOB_ID = 0xB70B1D7; // 191934935L
+    /**
+     * Max time period (in millis) between each idle maintenance job run.
+     */
+    public static final long IDLE_JOB_PERIOD_MILLIS = TimeUnit.DAYS.toMillis(1);
+
+    /**
+     * Timeout in millis after which sessions with no updates will be deleted.
+     */
+    public static final long SESSION_EXPIRY_TIMEOUT_MILLIS = TimeUnit.DAYS.toMillis(7);
+
     @Nullable
     public static File prepareBlobFile(long sessionId) {
         final File blobsDir = prepareBlobsDir();
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreIdleJobService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreIdleJobService.java
new file mode 100644
index 0000000..460e776
--- /dev/null
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreIdleJobService.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2020 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.server.blob;
+
+import static com.android.server.blob.BlobStoreConfig.IDLE_JOB_ID;
+import static com.android.server.blob.BlobStoreConfig.IDLE_JOB_PERIOD_MILLIS;
+import static com.android.server.blob.BlobStoreConfig.LOGV;
+import static com.android.server.blob.BlobStoreConfig.TAG;
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.AsyncTask;
+import android.util.Slog;
+
+import com.android.server.LocalServices;
+
+/**
+ * Maintenance job to clean up stale sessions and blobs.
+ */
+public class BlobStoreIdleJobService extends JobService {
+    @Override
+    public boolean onStartJob(final JobParameters params) {
+        AsyncTask.execute(() -> {
+            final BlobStoreManagerInternal blobStoreManagerInternal = LocalServices.getService(
+                    BlobStoreManagerInternal.class);
+            blobStoreManagerInternal.onIdleMaintenance();
+            jobFinished(params, false);
+        });
+        return false;
+    }
+
+    @Override
+    public boolean onStopJob(final JobParameters params) {
+        Slog.d(TAG, "Idle maintenance job is stopped; id=" + params.getJobId()
+                + ", reason=" + JobParameters.getReasonCodeDescription(params.getStopReason()));
+        return false;
+    }
+
+    static void schedule(Context context) {
+        final JobScheduler jobScheduler = (JobScheduler) context.getSystemService(
+                Context.JOB_SCHEDULER_SERVICE);
+        final JobInfo job = new JobInfo.Builder(IDLE_JOB_ID,
+                new ComponentName(context, BlobStoreIdleJobService.class))
+                        .setRequiresDeviceIdle(true)
+                        .setRequiresCharging(true)
+                        .setPeriodic(IDLE_JOB_PERIOD_MILLIS)
+                        .build();
+        jobScheduler.schedule(job);
+        if (LOGV) {
+            Slog.v(TAG, "Scheduling the idle maintenance job");
+        }
+    }
+}
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerInternal.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerInternal.java
new file mode 100644
index 0000000..5358245
--- /dev/null
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerInternal.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2020 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.server.blob;
+
+/**
+ * BlobStoreManager local system service interface.
+ *
+ * Only for use within the system server.
+ */
+public abstract class BlobStoreManagerInternal {
+    /**
+     * Triggered from idle maintenance job to cleanup stale blobs and sessions.
+     */
+    public abstract void onIdleMaintenance();
+}
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
index 13f095e..0ba34ca 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -28,6 +28,8 @@
 import static android.os.UserHandle.USER_NULL;
 
 import static com.android.server.blob.BlobStoreConfig.CURRENT_XML_VERSION;
+import static com.android.server.blob.BlobStoreConfig.LOGV;
+import static com.android.server.blob.BlobStoreConfig.SESSION_EXPIRY_TIMEOUT_MILLIS;
 import static com.android.server.blob.BlobStoreConfig.TAG;
 import static com.android.server.blob.BlobStoreSession.STATE_ABANDONED;
 import static com.android.server.blob.BlobStoreSession.STATE_COMMITTED;
@@ -61,6 +63,7 @@
 import android.os.UserHandle;
 import android.os.UserManagerInternal;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.ExceptionUtils;
 import android.util.LongSparseArray;
@@ -94,8 +97,10 @@
 import java.io.PrintWriter;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * Service responsible for maintaining and facilitating access to data blobs published by apps.
@@ -115,6 +120,10 @@
     @GuardedBy("mBlobsLock")
     private final SparseArray<ArrayMap<BlobHandle, BlobMetadata>> mBlobsMap = new SparseArray<>();
 
+    // Contains all ids that are currently in use.
+    @GuardedBy("mBlobsLock")
+    private final ArraySet<Long> mKnownBlobIds = new ArraySet<>();
+
     private final Context mContext;
     private final Handler mHandler;
     private final Injector mInjector;
@@ -151,6 +160,7 @@
     @Override
     public void onStart() {
         publishBinderService(Context.BLOB_STORE_SERVICE, new Stub());
+        LocalServices.addService(BlobStoreManagerInternal.class, new LocalService());
 
         mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
         registerReceivers();
@@ -164,6 +174,8 @@
                 readBlobSessionsLocked(allPackages);
                 readBlobsInfoLocked(allPackages);
             }
+        } else if (phase == PHASE_BOOT_COMPLETED) {
+            BlobStoreIdleJobService.schedule(mContext);
         }
     }
 
@@ -215,6 +227,40 @@
         }
     }
 
+    @VisibleForTesting
+    void addKnownIdsForTest(long... knownIds) {
+        synchronized (mBlobsLock) {
+            for (long id : knownIds) {
+                mKnownBlobIds.add(id);
+            }
+        }
+    }
+
+    @VisibleForTesting
+    Set<Long> getKnownIdsForTest() {
+        synchronized (mBlobsLock) {
+            return mKnownBlobIds;
+        }
+    }
+
+    @GuardedBy("mBlobsLock")
+    private void addSessionForUserLocked(BlobStoreSession session, int userId) {
+        getUserSessionsLocked(userId).put(session.getSessionId(), session);
+        mKnownBlobIds.add(session.getSessionId());
+    }
+
+    @GuardedBy("mBlobsLock")
+    private void addBlobForUserLocked(BlobMetadata blobMetadata, int userId) {
+        addBlobForUserLocked(blobMetadata, getUserBlobsLocked(userId));
+    }
+
+    @GuardedBy("mBlobsLock")
+    private void addBlobForUserLocked(BlobMetadata blobMetadata,
+            ArrayMap<BlobHandle, BlobMetadata> userBlobs) {
+        userBlobs.put(blobMetadata.getBlobHandle(), blobMetadata);
+        mKnownBlobIds.add(blobMetadata.getBlobId());
+    }
+
     private long createSessionInternal(BlobHandle blobHandle,
             int callingUid, String callingPackage) {
         synchronized (mBlobsLock) {
@@ -223,7 +269,11 @@
             final BlobStoreSession session = new BlobStoreSession(mContext,
                     sessionId, blobHandle, callingUid, callingPackage,
                     mSessionStateChangeListener);
-            getUserSessionsLocked(UserHandle.getUserId(callingUid)).put(sessionId, session);
+            addSessionForUserLocked(session, UserHandle.getUserId(callingUid));
+            if (LOGV) {
+                Slog.v(TAG, "Created session for " + blobHandle
+                        + "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
+            }
             writeBlobSessionsAsync();
             return sessionId;
         }
@@ -251,7 +301,10 @@
                     callingUid, callingPackage);
             session.open();
             session.abandon();
-
+            if (LOGV) {
+                Slog.v(TAG, "Deleted session with id " + sessionId
+                        + "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
+            }
             writeBlobSessionsAsync();
         }
     }
@@ -286,6 +339,10 @@
             }
             blobMetadata.addLeasee(callingPackage, callingUid,
                     descriptionResId, leaseExpiryTimeMillis);
+            if (LOGV) {
+                Slog.v(TAG, "Acquired lease on " + blobHandle
+                        + "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
+            }
             writeBlobsInfoAsync();
         }
     }
@@ -301,6 +358,10 @@
                         + "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
             }
             blobMetadata.removeLeasee(callingPackage, callingUid);
+            if (LOGV) {
+                Slog.v(TAG, "Released lease on " + blobHandle
+                        + "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
+            }
             writeBlobsInfoAsync();
         }
     }
@@ -329,6 +390,10 @@
                     session.getSessionFile().delete();
                     getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid()))
                             .remove(session.getSessionId());
+                    mKnownBlobIds.remove(session.getSessionId());
+                    if (LOGV) {
+                        Slog.v(TAG, "Session is invalid; deleted " + session);
+                    }
                     break;
                 case STATE_COMMITTED:
                     session.verifyBlobData();
@@ -340,7 +405,7 @@
                     if (blob == null) {
                         blob = new BlobMetadata(mContext,
                                 session.getSessionId(), session.getBlobHandle(), userId);
-                        userBlobs.put(session.getBlobHandle(), blob);
+                        addBlobForUserLocked(blob, userBlobs);
                     }
                     final Committer newCommitter = new Committer(session.getOwnerPackageName(),
                             session.getOwnerUid(), session.getBlobAccessMode());
@@ -355,6 +420,9 @@
                     }
                     getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid()))
                             .remove(session.getSessionId());
+                    if (LOGV) {
+                        Slog.v(TAG, "Successfully committed session " + session);
+                    }
                     break;
                 default:
                     Slog.wtf(TAG, "Invalid session state: "
@@ -397,6 +465,9 @@
             out.endTag(null, TAG_SESSIONS);
             out.endDocument();
             sessionsIndexFile.finishWrite(fos);
+            if (LOGV) {
+                Slog.v(TAG, "Finished persisting sessions data");
+            }
         } catch (Exception e) {
             sessionsIndexFile.failWrite(fos);
             Slog.wtf(TAG, "Error writing sessions data", e);
@@ -437,8 +508,8 @@
                     if (userPackages != null
                             && session.getOwnerPackageName().equals(
                                     userPackages.get(session.getOwnerUid()))) {
-                        getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid())).put(
-                                session.getSessionId(), session);
+                        addSessionForUserLocked(session,
+                                UserHandle.getUserId(session.getOwnerUid()));
                     } else {
                         // Unknown package or the session data does not belong to this package.
                         session.getSessionFile().delete();
@@ -446,6 +517,9 @@
                     mCurrentMaxSessionId = Math.max(mCurrentMaxSessionId, session.getSessionId());
                 }
             }
+            if (LOGV) {
+                Slog.v(TAG, "Finished reading sessions data");
+            }
         } catch (Exception e) {
             Slog.wtf(TAG, "Error reading sessions data", e);
         }
@@ -479,6 +553,9 @@
             out.endTag(null, TAG_BLOBS);
             out.endDocument();
             blobsIndexFile.finishWrite(fos);
+            if (LOGV) {
+                Slog.v(TAG, "Finished persisting blobs data");
+            }
         } catch (Exception e) {
             blobsIndexFile.failWrite(fos);
             Slog.wtf(TAG, "Error writing blobs data", e);
@@ -510,18 +587,21 @@
 
                 if (TAG_BLOB.equals(in.getName())) {
                     final BlobMetadata blobMetadata = BlobMetadata.createFromXml(mContext, in);
-                    final SparseArray<String> userPackages = allPackages.get(blobMetadata.userId);
+                    final SparseArray<String> userPackages = allPackages.get(
+                            blobMetadata.getUserId());
                     if (userPackages == null) {
                         blobMetadata.getBlobFile().delete();
                     } else {
-                        getUserBlobsLocked(blobMetadata.userId).put(
-                                blobMetadata.blobHandle, blobMetadata);
+                        addBlobForUserLocked(blobMetadata, blobMetadata.getUserId());
                         blobMetadata.removeInvalidCommitters(userPackages);
                         blobMetadata.removeInvalidLeasees(userPackages);
                     }
-                    mCurrentMaxSessionId = Math.max(mCurrentMaxSessionId, blobMetadata.blobId);
+                    mCurrentMaxSessionId = Math.max(mCurrentMaxSessionId, blobMetadata.getBlobId());
                 }
             }
+            if (LOGV) {
+                Slog.v(TAG, "Finished reading blobs data");
+            }
         } catch (Exception e) {
             Slog.wtf(TAG, "Error reading blobs data", e);
         }
@@ -614,6 +694,7 @@
                 if (session.getOwnerUid() == uid
                         && session.getOwnerPackageName().equals(packageName)) {
                     session.getSessionFile().delete();
+                    mKnownBlobIds.remove(session.getSessionId());
                     indicesToRemove.add(i);
                 }
             }
@@ -633,6 +714,7 @@
                 // Delete the blob if it doesn't have any active leases.
                 if (!blobMetadata.hasLeases()) {
                     blobMetadata.getBlobFile().delete();
+                    mKnownBlobIds.remove(blobMetadata.getBlobId());
                     indicesToRemove.add(i);
                 }
             }
@@ -640,6 +722,10 @@
                 userBlobs.removeAt(indicesToRemove.get(i));
             }
             writeBlobsInfoAsync();
+            if (LOGV) {
+                Slog.v(TAG, "Removed blobs data associated with pkg="
+                        + packageName + ", uid=" + uid);
+            }
         }
     }
 
@@ -651,6 +737,7 @@
                 for (int i = 0, count = userSessions.size(); i < count; ++i) {
                     final BlobStoreSession session = userSessions.valueAt(i);
                     session.getSessionFile().delete();
+                    mKnownBlobIds.remove(session.getSessionId());
                 }
             }
 
@@ -660,11 +747,107 @@
                 for (int i = 0, count = userBlobs.size(); i < count; ++i) {
                     final BlobMetadata blobMetadata = userBlobs.valueAt(i);
                     blobMetadata.getBlobFile().delete();
+                    mKnownBlobIds.remove(blobMetadata.getBlobId());
                 }
             }
+            if (LOGV) {
+                Slog.v(TAG, "Removed blobs data in user " + userId);
+            }
         }
     }
 
+    @GuardedBy("mBlobsLock")
+    @VisibleForTesting
+    void handleIdleMaintenanceLocked() {
+        // Cleanup any left over data on disk that is not part of index.
+        final ArrayList<Long> deletedBlobIds = new ArrayList<>();
+        final ArrayList<File> filesToDelete = new ArrayList<>();
+        final File blobsDir = BlobStoreConfig.getBlobsDir();
+        if (blobsDir.exists()) {
+            for (File file : blobsDir.listFiles()) {
+                try {
+                    final long id = Long.parseLong(file.getName());
+                    if (mKnownBlobIds.indexOf(id) < 0) {
+                        filesToDelete.add(file);
+                        deletedBlobIds.add(id);
+                    }
+                } catch (NumberFormatException e) {
+                    Slog.wtf(TAG, "Error parsing the file name: " + file, e);
+                    filesToDelete.add(file);
+                }
+            }
+            for (int i = 0, count = filesToDelete.size(); i < count; ++i) {
+                filesToDelete.get(i).delete();
+            }
+        }
+
+        // Cleanup any stale blobs.
+        for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) {
+            final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i);
+            userBlobs.entrySet().removeIf(entry -> {
+                final BlobHandle blobHandle = entry.getKey();
+                final BlobMetadata blobMetadata = entry.getValue();
+                boolean shouldRemove = false;
+
+                // Cleanup expired data blobs.
+                if (blobHandle.isExpired()) {
+                    shouldRemove = true;
+                }
+
+                // Cleanup blobs with no active leases.
+                // TODO: Exclude blobs which were just committed.
+                if (!blobMetadata.hasLeases()) {
+                    shouldRemove = true;
+                }
+
+                if (shouldRemove) {
+                    blobMetadata.getBlobFile().delete();
+                    mKnownBlobIds.remove(blobMetadata.getBlobId());
+                    deletedBlobIds.add(blobMetadata.getBlobId());
+                }
+                return shouldRemove;
+            });
+        }
+        writeBlobsInfoAsync();
+
+        // Cleanup any stale sessions.
+        final ArrayList<Integer> indicesToRemove = new ArrayList<>();
+        for (int i = 0, userCount = mSessions.size(); i < userCount; ++i) {
+            final LongSparseArray<BlobStoreSession> userSessions = mSessions.valueAt(i);
+            indicesToRemove.clear();
+            for (int j = 0, sessionsCount = userSessions.size(); j < sessionsCount; ++j) {
+                final BlobStoreSession blobStoreSession = userSessions.valueAt(j);
+                boolean shouldRemove = false;
+
+                // Cleanup sessions which haven't been modified in a while.
+                if (blobStoreSession.getSessionFile().lastModified()
+                        < System.currentTimeMillis() - SESSION_EXPIRY_TIMEOUT_MILLIS) {
+                    shouldRemove = true;
+                }
+
+                // Cleanup sessions with already expired data.
+                if (blobStoreSession.getBlobHandle().isExpired()) {
+                    shouldRemove = true;
+                }
+
+                if (shouldRemove) {
+                    blobStoreSession.getSessionFile().delete();
+                    mKnownBlobIds.remove(blobStoreSession.getSessionId());
+                    indicesToRemove.add(j);
+                    deletedBlobIds.add(blobStoreSession.getSessionId());
+                }
+            }
+            for (int j = 0; j < indicesToRemove.size(); ++j) {
+                userSessions.removeAt(indicesToRemove.get(j));
+            }
+        }
+        if (LOGV) {
+            Slog.v(TAG, "Completed idle maintenance; deleted "
+                    + Arrays.toString(deletedBlobIds.toArray()));
+        }
+        writeBlobSessionsAsync();
+    }
+
     void runClearAllSessions(@UserIdInt int userId) {
         synchronized (mBlobsLock) {
             if (userId == UserHandle.USER_ALL) {
@@ -727,10 +910,10 @@
             fout.increaseIndent();
             for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) {
                 final BlobMetadata blobMetadata = userBlobs.valueAt(j);
-                if (!dumpArgs.shouldDumpBlob(blobMetadata.blobId)) {
+                if (!dumpArgs.shouldDumpBlob(blobMetadata.getBlobId())) {
                     continue;
                 }
-                fout.println("Blob #" + blobMetadata.blobId);
+                fout.println("Blob #" + blobMetadata.getBlobId());
                 fout.increaseIndent();
                 blobMetadata.dump(fout, dumpArgs);
                 fout.decreaseIndent();
@@ -742,6 +925,9 @@
     private class PackageChangedReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
+            if (LOGV) {
+                Slog.v(TAG, "Received " + intent);
+            }
             switch (intent.getAction()) {
                 case Intent.ACTION_PACKAGE_FULLY_REMOVED:
                 case Intent.ACTION_PACKAGE_DATA_CLEARED:
@@ -893,6 +1079,14 @@
             final DumpArgs dumpArgs = DumpArgs.parse(args);
 
             final IndentingPrintWriter fout = new IndentingPrintWriter(writer, "    ");
+            if (dumpArgs.shouldDumpHelp()) {
+                writer.println("dumpsys blob_store [options]:");
+                fout.increaseIndent();
+                dumpArgs.dumpArgsUsage(fout);
+                fout.decreaseIndent();
+                return;
+            }
+
             synchronized (mBlobsLock) {
                 fout.println("mCurrentMaxSessionId: " + mCurrentMaxSessionId);
                 fout.println();
@@ -926,6 +1120,7 @@
         private boolean mDumpOnlySelectedSections;
         private boolean mDumpSessions;
         private boolean mDumpBlobs;
+        private boolean mDumpHelp;
 
         public boolean shouldDumpSession(String packageName, int uid, long blobId) {
             if (!CollectionUtils.isEmpty(mDumpPackages)
@@ -971,6 +1166,10 @@
                     || mDumpUserIds.indexOf(userId) >= 0;
         }
 
+        public boolean shouldDumpHelp() {
+            return mDumpHelp;
+        }
+
         private DumpArgs() {}
 
         public static DumpArgs parse(String[] args) {
@@ -1000,6 +1199,8 @@
                     dumpArgs.mDumpUserIds.add(getIntArgRequired(args, ++i, "userId"));
                 } else if ("--blob".equals(opt) || "-b".equals(opt)) {
                     dumpArgs.mDumpBlobIds.add(getLongArgRequired(args, ++i, "blobId"));
+                } else if ("--help".equals(opt) || "-h".equals(opt)) {
+                    dumpArgs.mDumpHelp = true;
                 } else {
                     // Everything else is assumed to be blob ids.
                     dumpArgs.mDumpBlobIds.add(getLongArgRequired(args, i, "blobId"));
@@ -1040,6 +1241,40 @@
             }
             return value;
         }
+
+        private void dumpArgsUsage(IndentingPrintWriter pw) {
+            pw.println("--help | -h");
+            printWithIndent(pw, "Dump this help text");
+            pw.println("--sessions");
+            printWithIndent(pw, "Dump only the sessions info");
+            pw.println("--blobs");
+            printWithIndent(pw, "Dump only the committed blobs info");
+            pw.println("--package | -p [package-name]");
+            printWithIndent(pw, "Dump blobs info associated with the given package");
+            pw.println("--uid | -u [uid]");
+            printWithIndent(pw, "Dump blobs info associated with the given uid");
+            pw.println("--user [user-id]");
+            printWithIndent(pw, "Dump blobs info in the given user");
+            pw.println("--blob | -b [session-id | blob-id]");
+            printWithIndent(pw, "Dump blob info corresponding to the given ID");
+            pw.println("--full | -f");
+            printWithIndent(pw, "Dump full unredacted blobs data");
+        }
+
+        private void printWithIndent(IndentingPrintWriter pw, String str) {
+            pw.increaseIndent();
+            pw.println(str);
+            pw.decreaseIndent();
+        }
+    }
+
+    private class LocalService extends BlobStoreManagerInternal {
+        @Override
+        public void onIdleMaintenance() {
+            synchronized (mBlobsLock) {
+                handleIdleMaintenanceLocked();
+            }
+        }
     }
 
     @VisibleForTesting
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
index 54a2997..bd35b86 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
@@ -21,11 +21,13 @@
 import static android.app.blob.XmlTags.ATTR_UID;
 import static android.app.blob.XmlTags.TAG_ACCESS_MODE;
 import static android.app.blob.XmlTags.TAG_BLOB_HANDLE;
+import static android.os.Trace.TRACE_TAG_SYSTEM_SERVER;
 import static android.system.OsConstants.O_CREAT;
 import static android.system.OsConstants.O_RDONLY;
 import static android.system.OsConstants.O_RDWR;
 import static android.system.OsConstants.SEEK_SET;
 
+import static com.android.server.blob.BlobStoreConfig.LOGV;
 import static com.android.server.blob.BlobStoreConfig.TAG;
 
 import android.annotation.BytesLong;
@@ -40,6 +42,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.RevocableFileDescriptor;
+import android.os.Trace;
 import android.os.storage.StorageManager;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -381,15 +384,22 @@
     void verifyBlobData() {
         byte[] actualDigest = null;
         try {
+            Trace.traceBegin(TRACE_TAG_SYSTEM_SERVER,
+                    "computeBlobDigest-i" + mSessionId + "-l" + getSessionFile().length());
             actualDigest = FileUtils.digest(getSessionFile(), mBlobHandle.algorithm);
         } catch (IOException | NoSuchAlgorithmException e) {
             Slog.e(TAG, "Error computing the digest", e);
+        } finally {
+            Trace.traceEnd(TRACE_TAG_SYSTEM_SERVER);
         }
         synchronized (mSessionLock) {
             if (actualDigest != null && Arrays.equals(actualDigest, mBlobHandle.digest)) {
                 mState = STATE_VERIFIED_VALID;
                 // Commit callback will be sent once the data is persisted.
             } else {
+                if (LOGV) {
+                    Slog.v(TAG, "Digest of the data didn't match the given BlobHandle.digest");
+                }
                 mState = STATE_VERIFIED_INVALID;
                 sendCommitCallbackResult(COMMIT_RESULT_ERROR);
             }
@@ -447,6 +457,16 @@
         }
     }
 
+    @Override
+    public String toString() {
+        return "BlobStoreSession {"
+                + "id:" + mSessionId
+                + ",handle:" + mBlobHandle
+                + ",uid:" + mOwnerUid
+                + ",pkg:" + mOwnerPackageName
+                + "}";
+    }
+
     private void assertCallerIsOwner() {
         final int callingUid = Binder.getCallingUid();
         if (callingUid != mOwnerUid) {
diff --git a/api/current.txt b/api/current.txt
index 02fac79..81c4c92 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -35687,6 +35687,7 @@
     field public static final String INCREMENTAL;
     field public static final int PREVIEW_SDK_INT;
     field public static final String RELEASE;
+    field @NonNull public static final String RELEASE_OR_CODENAME;
     field @Deprecated public static final String SDK;
     field public static final int SDK_INT;
     field public static final String SECURITY_PATCH;
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 4b1ba02..16c0910 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -48,6 +48,8 @@
     void clearData(String pkg, int uid, boolean fromApp);
     void enqueueTextToast(String pkg, IBinder token, CharSequence text, int duration, int displayId, @nullable ITransientNotificationCallback callback);
     void enqueueToast(String pkg, IBinder token, ITransientNotification callback, int duration, int displayId);
+    // TODO(b/144152069): Remove this after assessing impact on dogfood.
+    void enqueueTextOrCustomToast(String pkg, IBinder token, ITransientNotification callback, int duration, int displayId, boolean isCustom);
     void cancelToast(String pkg, IBinder token);
     void finishToken(String pkg, IBinder token);
 
diff --git a/core/java/android/app/timedetector/ITimeDetectorService.aidl b/core/java/android/app/timedetector/ITimeDetectorService.aidl
index de8f470..5ead0c9 100644
--- a/core/java/android/app/timedetector/ITimeDetectorService.aidl
+++ b/core/java/android/app/timedetector/ITimeDetectorService.aidl
@@ -18,7 +18,7 @@
 
 import android.app.timedetector.ManualTimeSuggestion;
 import android.app.timedetector.NetworkTimeSuggestion;
-import android.app.timedetector.PhoneTimeSuggestion;
+import android.app.timedetector.TelephonyTimeSuggestion;
 
 /**
  * System private API to communicate with time detector service.
@@ -34,7 +34,7 @@
  * {@hide}
  */
 interface ITimeDetectorService {
-  void suggestPhoneTime(in PhoneTimeSuggestion timeSuggestion);
   void suggestManualTime(in ManualTimeSuggestion timeSuggestion);
   void suggestNetworkTime(in NetworkTimeSuggestion timeSuggestion);
+  void suggestTelephonyTime(in TelephonyTimeSuggestion timeSuggestion);
 }
diff --git a/core/java/android/app/timedetector/PhoneTimeSuggestion.aidl b/core/java/android/app/timedetector/TelephonyTimeSuggestion.aidl
similarity index 94%
rename from core/java/android/app/timedetector/PhoneTimeSuggestion.aidl
rename to core/java/android/app/timedetector/TelephonyTimeSuggestion.aidl
index f5e2405..d9b0386 100644
--- a/core/java/android/app/timedetector/PhoneTimeSuggestion.aidl
+++ b/core/java/android/app/timedetector/TelephonyTimeSuggestion.aidl
@@ -16,4 +16,4 @@
 
 package android.app.timedetector;
 
-parcelable PhoneTimeSuggestion;
+parcelable TelephonyTimeSuggestion;
diff --git a/core/java/android/app/timedetector/PhoneTimeSuggestion.java b/core/java/android/app/timedetector/TelephonyTimeSuggestion.java
similarity index 79%
rename from core/java/android/app/timedetector/PhoneTimeSuggestion.java
rename to core/java/android/app/timedetector/TelephonyTimeSuggestion.java
index 0133a44..c0e8957 100644
--- a/core/java/android/app/timedetector/PhoneTimeSuggestion.java
+++ b/core/java/android/app/timedetector/TelephonyTimeSuggestion.java
@@ -50,17 +50,17 @@
  *
  * @hide
  */
-public final class PhoneTimeSuggestion implements Parcelable {
+public final class TelephonyTimeSuggestion implements Parcelable {
 
     /** @hide */
-    public static final @NonNull Parcelable.Creator<PhoneTimeSuggestion> CREATOR =
-            new Parcelable.Creator<PhoneTimeSuggestion>() {
-                public PhoneTimeSuggestion createFromParcel(Parcel in) {
-                    return PhoneTimeSuggestion.createFromParcel(in);
+    public static final @NonNull Parcelable.Creator<TelephonyTimeSuggestion> CREATOR =
+            new Parcelable.Creator<TelephonyTimeSuggestion>() {
+                public TelephonyTimeSuggestion createFromParcel(Parcel in) {
+                    return TelephonyTimeSuggestion.createFromParcel(in);
                 }
 
-                public PhoneTimeSuggestion[] newArray(int size) {
-                    return new PhoneTimeSuggestion[size];
+                public TelephonyTimeSuggestion[] newArray(int size) {
+                    return new TelephonyTimeSuggestion[size];
                 }
             };
 
@@ -68,15 +68,15 @@
     @Nullable private final TimestampedValue<Long> mUtcTime;
     @Nullable private ArrayList<String> mDebugInfo;
 
-    private PhoneTimeSuggestion(Builder builder) {
+    private TelephonyTimeSuggestion(Builder builder) {
         mSlotIndex = builder.mSlotIndex;
         mUtcTime = builder.mUtcTime;
         mDebugInfo = builder.mDebugInfo != null ? new ArrayList<>(builder.mDebugInfo) : null;
     }
 
-    private static PhoneTimeSuggestion createFromParcel(Parcel in) {
+    private static TelephonyTimeSuggestion createFromParcel(Parcel in) {
         int slotIndex = in.readInt();
-        PhoneTimeSuggestion suggestion = new PhoneTimeSuggestion.Builder(slotIndex)
+        TelephonyTimeSuggestion suggestion = new TelephonyTimeSuggestion.Builder(slotIndex)
                 .setUtcTime(in.readParcelable(null /* classLoader */))
                 .build();
         @SuppressWarnings("unchecked")
@@ -102,7 +102,7 @@
     /**
      * Returns an identifier for the source of this suggestion.
      *
-     * <p>See {@link PhoneTimeSuggestion} for more information about {@code slotIndex}.
+     * <p>See {@link TelephonyTimeSuggestion} for more information about {@code slotIndex}.
      */
     public int getSlotIndex() {
         return mSlotIndex;
@@ -111,7 +111,7 @@
     /**
      * Returns the suggested time or {@code null} if there isn't one.
      *
-     * <p>See {@link PhoneTimeSuggestion} for more information about {@code utcTime}.
+     * <p>See {@link TelephonyTimeSuggestion} for more information about {@code utcTime}.
      */
     @Nullable
     public TimestampedValue<Long> getUtcTime() {
@@ -121,7 +121,7 @@
     /**
      * Returns debug metadata for the suggestion.
      *
-     * <p>See {@link PhoneTimeSuggestion} for more information about {@code debugInfo}.
+     * <p>See {@link TelephonyTimeSuggestion} for more information about {@code debugInfo}.
      */
     @NonNull
     public List<String> getDebugInfo() {
@@ -132,7 +132,7 @@
     /**
      * Associates information with the instance that can be useful for debugging / logging.
      *
-     * <p>See {@link PhoneTimeSuggestion} for more information about {@code debugInfo}.
+     * <p>See {@link TelephonyTimeSuggestion} for more information about {@code debugInfo}.
      */
     public void addDebugInfo(@NonNull String debugInfo) {
         if (mDebugInfo == null) {
@@ -144,7 +144,7 @@
     /**
      * Associates information with the instance that can be useful for debugging / logging.
      *
-     * <p>See {@link PhoneTimeSuggestion} for more information about {@code debugInfo}.
+     * <p>See {@link TelephonyTimeSuggestion} for more information about {@code debugInfo}.
      */
     public void addDebugInfo(@NonNull List<String> debugInfo) {
         if (mDebugInfo == null) {
@@ -161,7 +161,7 @@
         if (o == null || getClass() != o.getClass()) {
             return false;
         }
-        PhoneTimeSuggestion that = (PhoneTimeSuggestion) o;
+        TelephonyTimeSuggestion that = (TelephonyTimeSuggestion) o;
         return mSlotIndex == that.mSlotIndex
                 && Objects.equals(mUtcTime, that.mUtcTime);
     }
@@ -173,7 +173,7 @@
 
     @Override
     public String toString() {
-        return "PhoneTimeSuggestion{"
+        return "TelephonyTimeSuggestion{"
                 + "mSlotIndex='" + mSlotIndex + '\''
                 + ", mUtcTime=" + mUtcTime
                 + ", mDebugInfo=" + mDebugInfo
@@ -181,7 +181,7 @@
     }
 
     /**
-     * Builds {@link PhoneTimeSuggestion} instances.
+     * Builds {@link TelephonyTimeSuggestion} instances.
      *
      * @hide
      */
@@ -193,7 +193,7 @@
         /**
          * Creates a builder with the specified {@code slotIndex}.
          *
-         * <p>See {@link PhoneTimeSuggestion} for more information about {@code slotIndex}.
+         * <p>See {@link TelephonyTimeSuggestion} for more information about {@code slotIndex}.
          */
         public Builder(int slotIndex) {
             mSlotIndex = slotIndex;
@@ -202,7 +202,7 @@
         /**
          * Returns the builder for call chaining.
          *
-         * <p>See {@link PhoneTimeSuggestion} for more information about {@code utcTime}.
+         * <p>See {@link TelephonyTimeSuggestion} for more information about {@code utcTime}.
          */
         @NonNull
         public Builder setUtcTime(@Nullable TimestampedValue<Long> utcTime) {
@@ -218,7 +218,7 @@
         /**
          * Returns the builder for call chaining.
          *
-         * <p>See {@link PhoneTimeSuggestion} for more information about {@code debugInfo}.
+         * <p>See {@link TelephonyTimeSuggestion} for more information about {@code debugInfo}.
          */
         @NonNull
         public Builder addDebugInfo(@NonNull String debugInfo) {
@@ -229,10 +229,10 @@
             return this;
         }
 
-        /** Returns the {@link PhoneTimeSuggestion}. */
+        /** Returns the {@link TelephonyTimeSuggestion}. */
         @NonNull
-        public PhoneTimeSuggestion build() {
-            return new PhoneTimeSuggestion(this);
+        public TelephonyTimeSuggestion build() {
+            return new TelephonyTimeSuggestion(this);
         }
     }
 }
diff --git a/core/java/android/app/timedetector/TimeDetector.java b/core/java/android/app/timedetector/TimeDetector.java
index df4f513..84ad495 100644
--- a/core/java/android/app/timedetector/TimeDetector.java
+++ b/core/java/android/app/timedetector/TimeDetector.java
@@ -45,12 +45,12 @@
     }
 
     /**
-     * Suggests the current phone-signal derived time to the detector. The detector may ignore the
-     * signal if better signals are available such as those that come from more reliable sources or
-     * were determined more recently.
+     * Suggests a telephony-signal derived time to the detector. The detector may ignore the signal
+     * if better signals are available such as those that come from more reliable sources or were
+     * determined more recently.
      */
-    @RequiresPermission(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE)
-    void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion);
+    @RequiresPermission(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE)
+    void suggestTelephonyTime(@NonNull TelephonyTimeSuggestion timeSuggestion);
 
     /**
      * Suggests the user's manually entered current time to the detector.
diff --git a/core/java/android/app/timedetector/TimeDetectorImpl.java b/core/java/android/app/timedetector/TimeDetectorImpl.java
index 1683817..c1d6667 100644
--- a/core/java/android/app/timedetector/TimeDetectorImpl.java
+++ b/core/java/android/app/timedetector/TimeDetectorImpl.java
@@ -40,12 +40,12 @@
     }
 
     @Override
-    public void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) {
+    public void suggestTelephonyTime(@NonNull TelephonyTimeSuggestion timeSuggestion) {
         if (DEBUG) {
-            Log.d(TAG, "suggestPhoneTime called: " + timeSuggestion);
+            Log.d(TAG, "suggestTelephonyTime called: " + timeSuggestion);
         }
         try {
-            mITimeDetectorService.suggestPhoneTime(timeSuggestion);
+            mITimeDetectorService.suggestTelephonyTime(timeSuggestion);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
index df643831..b06f4b8 100644
--- a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
+++ b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
@@ -17,7 +17,7 @@
 package android.app.timezonedetector;
 
 import android.app.timezonedetector.ManualTimeZoneSuggestion;
-import android.app.timezonedetector.PhoneTimeZoneSuggestion;
+import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
 
 /**
  * System private API to communicate with time zone detector service.
@@ -34,5 +34,5 @@
  */
 interface ITimeZoneDetectorService {
   void suggestManualTimeZone(in ManualTimeZoneSuggestion timeZoneSuggestion);
-  void suggestPhoneTimeZone(in PhoneTimeZoneSuggestion timeZoneSuggestion);
+  void suggestTelephonyTimeZone(in TelephonyTimeZoneSuggestion timeZoneSuggestion);
 }
diff --git a/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.aidl b/core/java/android/app/timezonedetector/TelephonyTimeZoneSuggestion.aidl
similarity index 94%
rename from core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.aidl
rename to core/java/android/app/timezonedetector/TelephonyTimeZoneSuggestion.aidl
index 3ad903b..b57ad20 100644
--- a/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.aidl
+++ b/core/java/android/app/timezonedetector/TelephonyTimeZoneSuggestion.aidl
@@ -16,4 +16,4 @@
 
 package android.app.timezonedetector;
 
-parcelable PhoneTimeZoneSuggestion;
+parcelable TelephonyTimeZoneSuggestion;
diff --git a/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java b/core/java/android/app/timezonedetector/TelephonyTimeZoneSuggestion.java
similarity index 84%
rename from core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java
rename to core/java/android/app/timezonedetector/TelephonyTimeZoneSuggestion.java
index 9147b44..150c01d 100644
--- a/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java
+++ b/core/java/android/app/timezonedetector/TelephonyTimeZoneSuggestion.java
@@ -56,18 +56,18 @@
  *
  * @hide
  */
-public final class PhoneTimeZoneSuggestion implements Parcelable {
+public final class TelephonyTimeZoneSuggestion implements Parcelable {
 
     /** @hide */
     @NonNull
-    public static final Creator<PhoneTimeZoneSuggestion> CREATOR =
-            new Creator<PhoneTimeZoneSuggestion>() {
-                public PhoneTimeZoneSuggestion createFromParcel(Parcel in) {
-                    return PhoneTimeZoneSuggestion.createFromParcel(in);
+    public static final Creator<TelephonyTimeZoneSuggestion> CREATOR =
+            new Creator<TelephonyTimeZoneSuggestion>() {
+                public TelephonyTimeZoneSuggestion createFromParcel(Parcel in) {
+                    return TelephonyTimeZoneSuggestion.createFromParcel(in);
                 }
 
-                public PhoneTimeZoneSuggestion[] newArray(int size) {
-                    return new PhoneTimeZoneSuggestion[size];
+                public TelephonyTimeZoneSuggestion[] newArray(int size) {
+                    return new TelephonyTimeZoneSuggestion[size];
                 }
             };
 
@@ -76,7 +76,7 @@
      * the same {@code slotIndex}.
      */
     @NonNull
-    public static PhoneTimeZoneSuggestion createEmptySuggestion(
+    public static TelephonyTimeZoneSuggestion createEmptySuggestion(
             int slotIndex, @NonNull String debugInfo) {
         return new Builder(slotIndex).addDebugInfo(debugInfo).build();
     }
@@ -144,7 +144,7 @@
     @Quality private final int mQuality;
     @Nullable private List<String> mDebugInfo;
 
-    private PhoneTimeZoneSuggestion(Builder builder) {
+    private TelephonyTimeZoneSuggestion(Builder builder) {
         mSlotIndex = builder.mSlotIndex;
         mZoneId = builder.mZoneId;
         mMatchType = builder.mMatchType;
@@ -153,15 +153,16 @@
     }
 
     @SuppressWarnings("unchecked")
-    private static PhoneTimeZoneSuggestion createFromParcel(Parcel in) {
+    private static TelephonyTimeZoneSuggestion createFromParcel(Parcel in) {
         // Use the Builder so we get validation during build().
         int slotIndex = in.readInt();
-        PhoneTimeZoneSuggestion suggestion = new Builder(slotIndex)
+        TelephonyTimeZoneSuggestion suggestion = new Builder(slotIndex)
                 .setZoneId(in.readString())
                 .setMatchType(in.readInt())
                 .setQuality(in.readInt())
                 .build();
-        List<String> debugInfo = in.readArrayList(PhoneTimeZoneSuggestion.class.getClassLoader());
+        List<String> debugInfo =
+                in.readArrayList(TelephonyTimeZoneSuggestion.class.getClassLoader());
         if (debugInfo != null) {
             suggestion.addDebugInfo(debugInfo);
         }
@@ -185,7 +186,7 @@
     /**
      * Returns an identifier for the source of this suggestion.
      *
-     * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code slotIndex}.
+     * <p>See {@link TelephonyTimeZoneSuggestion} for more information about {@code slotIndex}.
      */
     public int getSlotIndex() {
         return mSlotIndex;
@@ -195,7 +196,7 @@
      * Returns the suggested time zone Olson ID, e.g. "America/Los_Angeles". {@code null} means that
      * the caller is no longer sure what the current time zone is.
      *
-     * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code zoneId}.
+     * <p>See {@link TelephonyTimeZoneSuggestion} for more information about {@code zoneId}.
      */
     @Nullable
     public String getZoneId() {
@@ -206,7 +207,7 @@
      * Returns information about how the suggestion was determined which could be used to rank
      * suggestions when several are available from different sources.
      *
-     * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code matchType}.
+     * <p>See {@link TelephonyTimeZoneSuggestion} for more information about {@code matchType}.
      */
     @MatchType
     public int getMatchType() {
@@ -216,7 +217,7 @@
     /**
      * Returns information about the likelihood of the suggested zone being correct.
      *
-     * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code quality}.
+     * <p>See {@link TelephonyTimeZoneSuggestion} for more information about {@code quality}.
      */
     @Quality
     public int getQuality() {
@@ -226,7 +227,7 @@
     /**
      * Returns debug metadata for the suggestion.
      *
-     * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code debugInfo}.
+     * <p>See {@link TelephonyTimeZoneSuggestion} for more information about {@code debugInfo}.
      */
     @NonNull
     public List<String> getDebugInfo() {
@@ -237,7 +238,7 @@
     /**
      * Associates information with the instance that can be useful for debugging / logging.
      *
-     * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code debugInfo}.
+     * <p>See {@link TelephonyTimeZoneSuggestion} for more information about {@code debugInfo}.
      */
     public void addDebugInfo(@NonNull String debugInfo) {
         if (mDebugInfo == null) {
@@ -249,7 +250,7 @@
     /**
      * Associates information with the instance that can be useful for debugging / logging.
      *
-     * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code debugInfo}.
+     * <p>See {@link TelephonyTimeZoneSuggestion} for more information about {@code debugInfo}.
      */
     public void addDebugInfo(@NonNull List<String> debugInfo) {
         if (mDebugInfo == null) {
@@ -266,7 +267,7 @@
         if (o == null || getClass() != o.getClass()) {
             return false;
         }
-        PhoneTimeZoneSuggestion that = (PhoneTimeZoneSuggestion) o;
+        TelephonyTimeZoneSuggestion that = (TelephonyTimeZoneSuggestion) o;
         return mSlotIndex == that.mSlotIndex
                 && mMatchType == that.mMatchType
                 && mQuality == that.mQuality
@@ -280,7 +281,7 @@
 
     @Override
     public String toString() {
-        return "PhoneTimeZoneSuggestion{"
+        return "TelephonyTimeZoneSuggestion{"
                 + "mSlotIndex=" + mSlotIndex
                 + ", mZoneId='" + mZoneId + '\''
                 + ", mMatchType=" + mMatchType
@@ -290,7 +291,7 @@
     }
 
     /**
-     * Builds {@link PhoneTimeZoneSuggestion} instances.
+     * Builds {@link TelephonyTimeZoneSuggestion} instances.
      *
      * @hide
      */
@@ -304,7 +305,7 @@
         /**
          * Creates a builder with the specified {@code slotIndex}.
          *
-         * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code slotIndex}.
+         * <p>See {@link TelephonyTimeZoneSuggestion} for more information about {@code slotIndex}.
          */
         public Builder(int slotIndex) {
             mSlotIndex = slotIndex;
@@ -313,7 +314,7 @@
         /**
          * Returns the builder for call chaining.
          *
-         * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code zoneId}.
+         * <p>See {@link TelephonyTimeZoneSuggestion} for more information about {@code zoneId}.
          */
         @NonNull
         public Builder setZoneId(@Nullable String zoneId) {
@@ -324,7 +325,7 @@
         /**
          * Returns the builder for call chaining.
          *
-         * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code matchType}.
+         * <p>See {@link TelephonyTimeZoneSuggestion} for more information about {@code matchType}.
          */
         @NonNull
         public Builder setMatchType(@MatchType int matchType) {
@@ -335,7 +336,7 @@
         /**
          * Returns the builder for call chaining.
          *
-         * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code quality}.
+         * <p>See {@link TelephonyTimeZoneSuggestion} for more information about {@code quality}.
          */
         @NonNull
         public Builder setQuality(@Quality int quality) {
@@ -346,7 +347,7 @@
         /**
          * Returns the builder for call chaining.
          *
-         * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code debugInfo}.
+         * <p>See {@link TelephonyTimeZoneSuggestion} for more information about {@code debugInfo}.
          */
         @NonNull
         public Builder addDebugInfo(@NonNull String debugInfo) {
@@ -384,11 +385,11 @@
             }
         }
 
-        /** Returns the {@link PhoneTimeZoneSuggestion}. */
+        /** Returns the {@link TelephonyTimeZoneSuggestion}. */
         @NonNull
-        public PhoneTimeZoneSuggestion build() {
+        public TelephonyTimeZoneSuggestion build() {
             validate();
-            return new PhoneTimeZoneSuggestion(this);
+            return new TelephonyTimeZoneSuggestion(this);
         }
     }
 }
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java
index 6a3953e..20761ad 100644
--- a/core/java/android/app/timezonedetector/TimeZoneDetector.java
+++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java
@@ -47,8 +47,8 @@
      *
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE)
-    void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion timeZoneSuggestion);
+    @RequiresPermission(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE)
+    void suggestTelephonyTimeZone(@NonNull TelephonyTimeZoneSuggestion timeZoneSuggestion);
 
     /**
      * Suggests the current time zone, determined for the user's manually information, to the
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java b/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java
index 27b8374..0ada885 100644
--- a/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java
+++ b/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java
@@ -40,12 +40,12 @@
     }
 
     @Override
-    public void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion timeZoneSuggestion) {
+    public void suggestTelephonyTimeZone(@NonNull TelephonyTimeZoneSuggestion timeZoneSuggestion) {
         if (DEBUG) {
-            Log.d(TAG, "suggestPhoneTimeZone called: " + timeZoneSuggestion);
+            Log.d(TAG, "suggestTelephonyTimeZone called: " + timeZoneSuggestion);
         }
         try {
-            mITimeZoneDetectorService.suggestPhoneTimeZone(timeZoneSuggestion);
+            mITimeZoneDetectorService.suggestTelephonyTimeZone(timeZoneSuggestion);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index 0549c34..6f30ecd 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -15,5 +15,15 @@
       "name": "FrameworksInstantAppResolverTests",
       "file_patterns": ["(/|^)InstantApp[^/]*"]
     }
+  ],
+  "postsubmit": [
+    {
+      "name": "CtsAppSecurityHostTestCases",
+      "options": [
+        {
+          "include-filter": "android.appsecurity.cts.AppSecurityTests#testPermissionDiffCert"
+        }
+      ]
+    }
   ]
 }
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index a291734..70b2db7 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -240,6 +240,13 @@
         public static final String RELEASE = getString("ro.build.version.release");
 
         /**
+         * The version string we show to the user; may be {@link #RELEASE} or
+         * {@link #CODENAME} if not a final release build.
+         */
+        @NonNull public static final String RELEASE_OR_CODENAME = getString(
+                "ro.build.version.release_or_codename");
+
+        /**
          * The base OS build the product is based on.
          */
         public static final String BASE_OS = SystemProperties.get("ro.build.version.base_os", "");
diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java
index 8990bdf..35fa37a 100644
--- a/core/java/android/os/incremental/IncrementalManager.java
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -23,8 +23,6 @@
 import android.content.Context;
 import android.content.pm.DataLoaderParams;
 import android.os.RemoteException;
-import android.system.ErrnoException;
-import android.system.Os;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
@@ -193,116 +191,54 @@
     }
 
     /**
-     * Renames an Incremental path to a new path. If source path is a file, make a link from the old
-     * Incremental file to the new one. If source path is a dir, unbind old dir from Incremental
-     * Storage and bind the new one.
-     * <ol>
-     *     <li> For renaming a dir, dest dir will be created if not exists, and does not need to
-     *          be on the same Incremental storage as the source. </li>
-     *     <li> For renaming a file, dest file must be on the same Incremental storage as source.
-     *     </li>
-     * </ol>
+     * Set up an app's code path. The expected outcome of this method is:
+     * 1) The actual apk directory under /data/incremental is bind-mounted to the parent directory
+     * of {@code afterCodeFile}.
+     * 2) All the files under {@code beforeCodeFile} will show up under {@code afterCodeFile}.
      *
-     * @param sourcePath Absolute path to the source. Should be the same type as the destPath (file
-     *                   or dir). Expected to already exist and is an Incremental path.
-     * @param destPath   Absolute path to the destination.
-     * @throws IllegalArgumentException when 1) source does not exist, or 2) source and dest type
-     *                                  mismatch (one is file and the other is dir), or 3) source
-     *                                  path is not on Incremental File System,
-     * @throws IOException              when 1) cannot find the root path of the Incremental storage
-     *                                  of source, or 2) cannot retrieve the Incremental storage
-     *                                  instance of the source, or 3) renaming a file, but dest is
-     *                                  not on the same Incremental Storage, or 4) renaming a dir,
-     *                                  dest dir does not exist but fails to be created.
-     *                                  <p>
-     *                                  TODO(b/136132412): add unit tests
+     * @param beforeCodeFile Path that is currently bind-mounted and have APKs under it.
+     *                       Should no longer have any APKs after this method is called.
+     *                       Example: /data/app/vmdl*tmp
+     * @param afterCodeFile Path that should will have APKs after this method is called. Its parent
+     *                      directory should be bind-mounted to a directory under /data/incremental.
+     *                      Example: /data/app/~~[randomStringA]/[packageName]-[randomStringB]
+     * @throws IllegalArgumentException
+     * @throws IOException
+     * TODO(b/147371381): add unit tests
      */
-    public void rename(@NonNull String sourcePath, @NonNull String destPath) throws IOException {
-        final File source = new File(sourcePath);
-        final File dest = new File(destPath);
-        if (!source.exists()) {
-            throw new IllegalArgumentException("Path not exist: " + sourcePath);
+    public void renameCodePath(File beforeCodeFile, File afterCodeFile)
+            throws IllegalArgumentException, IOException {
+        final String beforeCodePath = beforeCodeFile.getAbsolutePath();
+        final String afterCodePathParent = afterCodeFile.getParentFile().getAbsolutePath();
+        if (!isIncrementalPath(beforeCodePath)) {
+            throw new IllegalArgumentException("Not an Incremental path: " + beforeCodePath);
         }
-        if (dest.exists()) {
-            throw new IllegalArgumentException("Target path already exists: " + destPath);
+        final String afterCodePathName = afterCodeFile.getName();
+        final Path apkStoragePath = Paths.get(beforeCodePath);
+        if (apkStoragePath == null || apkStoragePath.toAbsolutePath() == null) {
+            throw new IOException("Invalid source storage path for: " + beforeCodePath);
         }
-        if (source.isDirectory() && dest.exists() && dest.isFile()) {
-            throw new IllegalArgumentException(
-                    "Trying to rename a dir but destination is a file: " + destPath);
-        }
-        if (source.isFile() && dest.exists() && dest.isDirectory()) {
-            throw new IllegalArgumentException(
-                    "Trying to rename a file but destination is a dir: " + destPath);
-        }
-        if (!isIncrementalPath(sourcePath)) {
-            throw new IllegalArgumentException("Not an Incremental path: " + sourcePath);
-        }
-
-        Path storagePath = Paths.get(sourcePath);
-        if (source.isFile()) {
-            storagePath = getStoragePathForFile(source);
-        }
-        if (storagePath == null || storagePath.toAbsolutePath() == null) {
-            throw new IOException("Invalid source storage path for: " + sourcePath);
-        }
-        final IncrementalStorage storage = openStorage(storagePath.toAbsolutePath().toString());
-        if (storage == null) {
+        final IncrementalStorage apkStorage =
+                openStorage(apkStoragePath.toAbsolutePath().toString());
+        if (apkStorage == null) {
             throw new IOException("Failed to retrieve storage from Incremental Service.");
         }
-
-        if (source.isFile()) {
-            renameFile(storage, storagePath, source, dest);
-        } else {
-            renameDir(storage, storagePath, source, dest);
+        final IncrementalStorage linkedApkStorage = createStorage(afterCodePathParent, apkStorage,
+                IncrementalManager.CREATE_MODE_CREATE
+                        | IncrementalManager.CREATE_MODE_PERMANENT_BIND);
+        if (linkedApkStorage == null) {
+            throw new IOException("Failed to create linked storage at dir: " + afterCodePathParent);
         }
-    }
-
-    private void renameFile(IncrementalStorage storage, Path storagePath,
-            File source, File dest) throws IOException {
-        Path sourcePath = source.toPath();
-        Path destPath = dest.toPath();
-        if (!sourcePath.startsWith(storagePath)) {
-            throw new IOException("Path: " + source.getAbsolutePath() + " is not on storage at: "
-                    + storagePath.toString());
-        }
-        if (!destPath.startsWith(storagePath)) {
-            throw new IOException("Path: " + dest.getAbsolutePath() + " is not on storage at: "
-                    + storagePath.toString());
-        }
-        final Path sourceRelativePath = storagePath.relativize(sourcePath);
-        final Path destRelativePath = storagePath.relativize(destPath);
-        storage.moveFile(sourceRelativePath.toString(), destRelativePath.toString());
-
-    }
-
-    private void renameDir(IncrementalStorage storage, Path storagePath,
-            File source, File dest) throws IOException {
-        Path destPath = dest.toPath();
-        boolean usedMkdir = false;
-        try {
-            Os.mkdir(dest.getAbsolutePath(), 0755);
-            usedMkdir = true;
-        } catch (ErrnoException e) {
-            // Traditional mkdir fails but maybe we can create it on Incremental File System if
-            // the dest path is on the same Incremental storage as the source.
-            if (destPath.startsWith(storagePath)) {
-                storage.makeDirectories(storagePath.relativize(destPath).toString());
-            } else {
-                throw new IOException("Failed to create directory: " + dest.getAbsolutePath(), e);
+        linkedApkStorage.makeDirectory(afterCodePathName);
+        File[] files = beforeCodeFile.listFiles();
+        for (int i = 0; i < files.length; i++) {
+            if (files[i].isFile()) {
+                String fileName = files[i].getName();
+                apkStorage.makeLink(
+                        fileName, linkedApkStorage, afterCodePathName + "/" + fileName);
             }
         }
-        try {
-            storage.moveDir(source.getAbsolutePath(), dest.getAbsolutePath());
-        } catch (Exception ex) {
-            if (usedMkdir) {
-                try {
-                    Os.remove(dest.getAbsolutePath());
-                } catch (ErrnoException ignored) {
-                }
-            }
-            throw new IOException(
-                    "Failed to move " + source.getAbsolutePath() + " to " + dest.getAbsolutePath());
-        }
+        apkStorage.unBind(beforeCodePath);
     }
 
     /**
diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java
index 848868a..3ff6f54 100644
--- a/core/java/android/service/textclassifier/TextClassifierService.java
+++ b/core/java/android/service/textclassifier/TextClassifierService.java
@@ -41,6 +41,7 @@
 import android.view.textclassifier.ConversationActions;
 import android.view.textclassifier.SelectionEvent;
 import android.view.textclassifier.TextClassification;
+import android.view.textclassifier.TextClassificationConstants;
 import android.view.textclassifier.TextClassificationContext;
 import android.view.textclassifier.TextClassificationManager;
 import android.view.textclassifier.TextClassificationSessionId;
@@ -404,22 +405,27 @@
      */
     @NonNull
     public static TextClassifier getDefaultTextClassifierImplementation(@NonNull Context context) {
-        final String defaultTextClassifierPackageName =
-                context.getPackageManager().getDefaultTextClassifierPackageName();
-        if (TextUtils.isEmpty(defaultTextClassifierPackageName)) {
-            return TextClassifier.NO_OP;
-        }
-        if (defaultTextClassifierPackageName.equals(context.getPackageName())) {
-            throw new RuntimeException(
-                    "The default text classifier itself should not call the"
-                            + "getDefaultTextClassifierImplementation() method.");
-        }
         final TextClassificationManager tcm =
                 context.getSystemService(TextClassificationManager.class);
-        if (tcm != null) {
-            return tcm.getTextClassifier(TextClassifier.DEFAULT_SERVICE);
+        if (tcm == null) {
+            return TextClassifier.NO_OP;
         }
-        return TextClassifier.NO_OP;
+        TextClassificationConstants settings = new TextClassificationConstants();
+        if (settings.getUseDefaultTextClassifierAsDefaultImplementation()) {
+            final String defaultTextClassifierPackageName =
+                    context.getPackageManager().getDefaultTextClassifierPackageName();
+            if (TextUtils.isEmpty(defaultTextClassifierPackageName)) {
+                return TextClassifier.NO_OP;
+            }
+            if (defaultTextClassifierPackageName.equals(context.getPackageName())) {
+                throw new RuntimeException(
+                        "The default text classifier itself should not call the"
+                                + "getDefaultTextClassifierImplementation() method.");
+            }
+            return tcm.getTextClassifier(TextClassifier.DEFAULT_SERVICE);
+        } else {
+            return tcm.getTextClassifier(TextClassifier.LOCAL);
+        }
     }
 
     /** @hide **/
diff --git a/core/java/android/view/textclassifier/TextClassificationConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java
index ed69513..3d5ac58 100644
--- a/core/java/android/view/textclassifier/TextClassificationConstants.java
+++ b/core/java/android/view/textclassifier/TextClassificationConstants.java
@@ -17,6 +17,7 @@
 package android.view.textclassifier;
 
 import android.annotation.Nullable;
+import android.content.Context;
 import android.provider.DeviceConfig;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -167,6 +168,16 @@
     static final String TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE =
             "textclassifier_service_package_override";
 
+    /**
+     * Whether to use the default system text classifier as the default text classifier
+     * implementation. The local text classifier is used if it is {@code false}.
+     *
+     * @see android.service.textclassifier.TextClassifierService#getDefaultTextClassifierImplementation(Context)
+     */
+    // TODO: Once the system health experiment is done, remove this together with local TC.
+    private static final String USE_DEFAULT_SYSTEM_TEXT_CLASSIFIER_AS_DEFAULT_IMPL =
+            "use_default_system_text_classifier_as_default_impl";
+
     private static final String DEFAULT_TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE = null;
     private static final boolean LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT = true;
     private static final boolean SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT = true;
@@ -209,7 +220,8 @@
     private static final boolean TEMPLATE_INTENT_FACTORY_ENABLED_DEFAULT = true;
     private static final boolean TRANSLATE_IN_CLASSIFICATION_ENABLED_DEFAULT = true;
     private static final boolean DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT = true;
-    private static final float[] LANG_ID_CONTEXT_SETTINGS_DEFAULT = new float[] {20f, 1.0f, 0.4f};
+    private static final float[] LANG_ID_CONTEXT_SETTINGS_DEFAULT = new float[]{20f, 1.0f, 0.4f};
+    private static final boolean USE_DEFAULT_SYSTEM_TEXT_CLASSIFIER_AS_DEFAULT_IMPL_DEFAULT = true;
 
     @Nullable
     public String getTextClassifierServicePackageOverride() {
@@ -331,6 +343,13 @@
                 LANG_ID_CONTEXT_SETTINGS, LANG_ID_CONTEXT_SETTINGS_DEFAULT);
     }
 
+    public boolean getUseDefaultTextClassifierAsDefaultImplementation() {
+        return DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+                USE_DEFAULT_SYSTEM_TEXT_CLASSIFIER_AS_DEFAULT_IMPL,
+                USE_DEFAULT_SYSTEM_TEXT_CLASSIFIER_AS_DEFAULT_IMPL_DEFAULT);
+    }
+
     void dump(IndentingPrintWriter pw) {
         pw.println("TextClassificationConstants:");
         pw.increaseIndent();
@@ -378,6 +397,8 @@
                 .println();
         pw.printPair("textclassifier_service_package_override",
                 getTextClassifierServicePackageOverride()).println();
+        pw.printPair("use_default_system_text_classifier_as_default_impl",
+                getUseDefaultTextClassifierAsDefaultImplementation()).println();
         pw.decreaseIndent();
     }
 
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index db714c2..a2c70b9 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -148,6 +148,9 @@
     @Nullable
     private CharSequence mText;
 
+    // TODO(b/144152069): Remove this after assessing impact on dogfood.
+    private boolean mIsCustomToast;
+
     /**
      * Construct an empty Toast object.  You must call {@link #setView} before you
      * can call {@link #show}.
@@ -214,7 +217,8 @@
                     service.enqueueTextToast(pkg, mToken, mText, mDuration, displayId, callback);
                 }
             } else {
-                service.enqueueToast(pkg, mToken, tn, mDuration, displayId);
+                service.enqueueTextOrCustomToast(pkg, mToken, tn, mDuration, displayId,
+                        mIsCustomToast);
             }
         } catch (RemoteException e) {
             // Empty
@@ -252,12 +256,17 @@
      */
     @Deprecated
     public void setView(View view) {
+        mIsCustomToast = true;
         mNextView = view;
     }
 
     /**
      * Return the view.
      *
+     * <p><strong>Warning:</strong> Starting from Android {@link Build.VERSION_CODES#R}, for apps
+     * targeting API level {@link Build.VERSION_CODES#R} or higher that haven't called {@link
+     * #setView(View)} with a non-{@code null} view, this method will return {@code null}.
+     *
      * @see #setView
      * @deprecated Custom toast views are deprecated. Apps can create a standard text toast with the
      *      {@link #makeText(Context, CharSequence, int)} method, or use a
@@ -293,6 +302,10 @@
     /**
      * Set the margins of the view.
      *
+     * <p><strong>Warning:</strong> Starting from Android {@link Build.VERSION_CODES#R}, for apps
+     * targeting API level {@link Build.VERSION_CODES#R} or higher, this method is a no-op when
+     * called on text toasts.
+     *
      * @param horizontalMargin The horizontal margin, in percentage of the
      *        container width, between the container's edges and the
      *        notification
@@ -301,30 +314,59 @@
      *        notification
      */
     public void setMargin(float horizontalMargin, float verticalMargin) {
+        if (isSystemRenderedTextToast()) {
+            Log.e(TAG, "setMargin() shouldn't be called on text toasts, the values won't be used");
+        }
         mTN.mHorizontalMargin = horizontalMargin;
         mTN.mVerticalMargin = verticalMargin;
     }
 
     /**
      * Return the horizontal margin.
+     *
+     * <p><strong>Warning:</strong> Starting from Android {@link Build.VERSION_CODES#R}, for apps
+     * targeting API level {@link Build.VERSION_CODES#R} or higher, this method shouldn't be called
+     * on text toasts as its return value may not reflect actual value since text toasts are not
+     * rendered by the app anymore.
      */
     public float getHorizontalMargin() {
+        if (isSystemRenderedTextToast()) {
+            Log.e(TAG, "getHorizontalMargin() shouldn't be called on text toasts, the result may "
+                    + "not reflect actual values.");
+        }
         return mTN.mHorizontalMargin;
     }
 
     /**
      * Return the vertical margin.
+     *
+     * <p><strong>Warning:</strong> Starting from Android {@link Build.VERSION_CODES#R}, for apps
+     * targeting API level {@link Build.VERSION_CODES#R} or higher, this method shouldn't be called
+     * on text toasts as its return value may not reflect actual value since text toasts are not
+     * rendered by the app anymore.
      */
     public float getVerticalMargin() {
+        if (isSystemRenderedTextToast()) {
+            Log.e(TAG, "getVerticalMargin() shouldn't be called on text toasts, the result may not"
+                    + " reflect actual values.");
+        }
         return mTN.mVerticalMargin;
     }
 
     /**
      * Set the location at which the notification should appear on the screen.
+     *
+     * <p><strong>Warning:</strong> Starting from Android {@link Build.VERSION_CODES#R}, for apps
+     * targeting API level {@link Build.VERSION_CODES#R} or higher, this method is a no-op when
+     * called on text toasts.
+     *
      * @see android.view.Gravity
      * @see #getGravity
      */
     public void setGravity(int gravity, int xOffset, int yOffset) {
+        if (isSystemRenderedTextToast()) {
+            Log.e(TAG, "setGravity() shouldn't be called on text toasts, the values won't be used");
+        }
         mTN.mGravity = gravity;
         mTN.mX = xOffset;
         mTN.mY = yOffset;
@@ -332,27 +374,59 @@
 
      /**
      * Get the location at which the notification should appear on the screen.
+     *
+     * <p><strong>Warning:</strong> Starting from Android {@link Build.VERSION_CODES#R}, for apps
+     * targeting API level {@link Build.VERSION_CODES#R} or higher, this method shouldn't be called
+     * on text toasts as its return value may not reflect actual value since text toasts are not
+     * rendered by the app anymore.
+     *
      * @see android.view.Gravity
      * @see #getGravity
      */
     public int getGravity() {
+        if (isSystemRenderedTextToast()) {
+            Log.e(TAG, "getGravity() shouldn't be called on text toasts, the result may not reflect"
+                    + " actual values.");
+        }
         return mTN.mGravity;
     }
 
     /**
      * Return the X offset in pixels to apply to the gravity's location.
+     *
+     * <p><strong>Warning:</strong> Starting from Android {@link Build.VERSION_CODES#R}, for apps
+     * targeting API level {@link Build.VERSION_CODES#R} or higher, this method shouldn't be called
+     * on text toasts as its return value may not reflect actual value since text toasts are not
+     * rendered by the app anymore.
      */
     public int getXOffset() {
+        if (isSystemRenderedTextToast()) {
+            Log.e(TAG, "getXOffset() shouldn't be called on text toasts, the result may not reflect"
+                    + " actual values.");
+        }
         return mTN.mX;
     }
 
     /**
      * Return the Y offset in pixels to apply to the gravity's location.
+     *
+     * <p><strong>Warning:</strong> Starting from Android {@link Build.VERSION_CODES#R}, for apps
+     * targeting API level {@link Build.VERSION_CODES#R} or higher, this method shouldn't be called
+     * on text toasts as its return value may not reflect actual value since text toasts are not
+     * rendered by the app anymore.
      */
     public int getYOffset() {
+        if (isSystemRenderedTextToast()) {
+            Log.e(TAG, "getYOffset() shouldn't be called on text toasts, the result may not reflect"
+                    + " actual values.");
+        }
         return mTN.mY;
     }
 
+    private boolean isSystemRenderedTextToast() {
+        return Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM) && mNextView == null;
+    }
+
     /**
      * Adds a callback to be notified when the toast is shown or hidden.
      *
@@ -386,7 +460,7 @@
     }
 
     /**
-     * Make a standard toast that just contains a text view.
+     * Make a standard toast that just contains text.
      *
      * @param context  The context to use.  Usually your {@link android.app.Application}
      *                 or {@link android.app.Activity} object.
@@ -429,7 +503,7 @@
     }
 
     /**
-     * Make a standard toast that just contains a text view with the text from a resource.
+     * Make a standard toast that just contains text from a resource.
      *
      * @param context  The context to use.  Usually your {@link android.app.Application}
      *                 or {@link android.app.Activity} object.
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index a43e4fe..65cad83 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -2424,6 +2424,10 @@
                     offset += findViewById(R.id.content_preview_container).getHeight();
                 }
 
+                if (hasWorkProfile() && ENABLE_TABBED_VIEW) {
+                    offset += findViewById(R.id.tabs).getHeight();
+                }
+
                 int directShareHeight = 0;
                 rowsToShow = Math.min(4, rowsToShow);
                 for (int i = 0, childCount = recyclerView.getChildCount();
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index c6ed624..518911e 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -323,7 +323,7 @@
         result.append(System.getProperty("java.vm.version")); // such as 1.1.0
         result.append(" (Linux; U; Android ");
 
-        String version = Build.VERSION.RELEASE; // "1.0" or "3.4b5"
+        String version = Build.VERSION.RELEASE_OR_CODENAME; // "1.0" or "3.4b5"
         result.append(version.length() > 0 ? version : "1.0");
 
         // add the model for the release build
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 9758673..35eb0fc 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -208,6 +208,7 @@
 
             static_libs: [
                 "libasync_safe",
+                "libbinderthreadstateutils",
                 "libdmabufinfo",
                 "libgif",
                 "libseccomp_policy",
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index c269d1c..14d7487 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -37,6 +37,7 @@
 #include <binder/Parcel.h>
 #include <binder/ProcessState.h>
 #include <binder/Stability.h>
+#include <binderthreadstate/CallerUtils.h>
 #include <cutils/atomic.h>
 #include <log/log.h>
 #include <utils/KeyedVector.h>
@@ -950,7 +951,7 @@
 
 static jboolean android_os_Binder_isHandlingTransaction()
 {
-    return IPCThreadState::self()->isServingCall();
+    return getCurrentServingCall() == BinderCallType::BINDER;
 }
 
 static jlong android_os_Binder_clearCallingIdentity()
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index efa7d59..32a6cc3 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2605,7 +2605,7 @@
          <p>Not for use by third-party applications.
          @hide
      -->
-    <permission android:name="android.permission.SUGGEST_PHONE_TIME_AND_ZONE"
+    <permission android:name="android.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE"
         android:protectionLevel="signature|telephony" />
 
     <!-- Allows applications like settings to suggest the user's manually chosen time / time zone.
@@ -4878,6 +4878,19 @@
     <permission android:name="android.permission.ACCESS_SHARED_LIBRARIES"
                 android:protectionLevel="signature|installer" />
 
+    <!-- Allows an app to log compat change usage.
+         @hide  <p>Not for use by third-party applications.</p> -->
+    <permission android:name="android.permission.LOG_COMPAT_CHANGE"
+                android:protectionLevel="signature|privileged" />
+    <!-- Allows an app to read compat change config.
+         @hide  <p>Not for use by third-party applications.</p> -->
+    <permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"
+                android:protectionLevel="signature|privileged" />
+    <!-- Allows an app to override compat change config.
+         @hide  <p>Not for use by third-party applications.</p> -->
+    <permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG"
+                android:protectionLevel="signature|privileged" />
+
     <!-- Allows input events to be monitored. Very dangerous!  @hide -->
     <permission android:name="android.permission.MONITOR_INPUT"
                 android:protectionLevel="signature" />
@@ -5315,6 +5328,10 @@
                  android:permission="android.permission.BIND_JOB_SERVICE" >
         </service>
 
+        <service android:name="com.android.server.blob.BlobStoreIdleJobService"
+                 android:permission="android.permission.BIND_JOB_SERVICE">
+        </service>
+
         <service android:name="com.android.server.pm.PackageManagerShellCommandDataLoader">
             <intent-filter>
                 <action android:name="android.intent.action.LOAD_DATA" />
diff --git a/core/res/res/layout/accessibility_button_chooser_item.xml b/core/res/res/layout/accessibility_button_chooser_item.xml
index d19e313..d6fd7aa 100644
--- a/core/res/res/layout/accessibility_button_chooser_item.xml
+++ b/core/res/res/layout/accessibility_button_chooser_item.xml
@@ -20,6 +20,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:clickable="true"
     android:gravity="center"
     android:paddingStart="16dp"
     android:paddingEnd="16dp"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b2d08a9..31e68e8 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4343,4 +4343,7 @@
          Determines whether the specified key groups can be used to wake up the device. -->
     <bool name="config_wakeOnDpadKeyPress">true</bool>
     <bool name="config_wakeOnAssistKeyPress">true</bool>
+
+    <!-- Whether to default to an expanded list of users on the lock screen user switcher. -->
+    <bool name="config_expandLockScreenUserSwitcher">false</bool>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c59d25f..2453bb1 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3863,4 +3863,7 @@
 
   <!-- Toast message for background started foreground service while-in-use permission restriction feature -->
   <java-symbol type="string" name="allow_while_in_use_permission_in_fgs" />
+
+  <!-- Whether to expand the lock screen user switcher by default -->
+  <java-symbol type="bool" name="config_expandLockScreenUserSwitcher" />
 </resources>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index ee93b39..59335a5 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -111,6 +111,10 @@
     <uses-permission android:name="android.permission.MOVE_PACKAGE" />
     <uses-permission android:name="android.permission.PACKAGE_VERIFICATION_AGENT" />
 
+    <!-- gating and logging permissions -->
+    <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE" />
+    <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
+
     <!-- os storage test permissions -->
     <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
     <uses-permission android:name="android.permission.ASEC_ACCESS" />
diff --git a/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java b/core/tests/coretests/src/android/app/timedetector/TelephonyTimeSuggestionTest.java
similarity index 66%
rename from core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java
rename to core/tests/coretests/src/android/app/timedetector/TelephonyTimeSuggestionTest.java
index d17b635..4b64dfc 100644
--- a/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java
+++ b/core/tests/coretests/src/android/app/timedetector/TelephonyTimeSuggestionTest.java
@@ -26,44 +26,45 @@
 
 import org.junit.Test;
 
-public class PhoneTimeSuggestionTest {
+public class TelephonyTimeSuggestionTest {
     private static final int SLOT_INDEX = 99999;
 
     @Test
     public void testEquals() {
-        PhoneTimeSuggestion.Builder builder1 = new PhoneTimeSuggestion.Builder(SLOT_INDEX);
+        TelephonyTimeSuggestion.Builder builder1 = new TelephonyTimeSuggestion.Builder(SLOT_INDEX);
         {
-            PhoneTimeSuggestion one = builder1.build();
+            TelephonyTimeSuggestion one = builder1.build();
             assertEquals(one, one);
         }
 
-        PhoneTimeSuggestion.Builder builder2 = new PhoneTimeSuggestion.Builder(SLOT_INDEX);
+        TelephonyTimeSuggestion.Builder builder2 = new TelephonyTimeSuggestion.Builder(SLOT_INDEX);
         {
-            PhoneTimeSuggestion one = builder1.build();
-            PhoneTimeSuggestion two = builder2.build();
+            TelephonyTimeSuggestion one = builder1.build();
+            TelephonyTimeSuggestion two = builder2.build();
             assertEquals(one, two);
             assertEquals(two, one);
         }
 
         builder1.setUtcTime(new TimestampedValue<>(1111L, 2222L));
         {
-            PhoneTimeSuggestion one = builder1.build();
+            TelephonyTimeSuggestion one = builder1.build();
             assertEquals(one, one);
         }
 
         builder2.setUtcTime(new TimestampedValue<>(1111L, 2222L));
         {
-            PhoneTimeSuggestion one = builder1.build();
-            PhoneTimeSuggestion two = builder2.build();
+            TelephonyTimeSuggestion one = builder1.build();
+            TelephonyTimeSuggestion two = builder2.build();
             assertEquals(one, two);
             assertEquals(two, one);
         }
 
-        PhoneTimeSuggestion.Builder builder3 = new PhoneTimeSuggestion.Builder(SLOT_INDEX + 1);
+        TelephonyTimeSuggestion.Builder builder3 =
+                new TelephonyTimeSuggestion.Builder(SLOT_INDEX + 1);
         builder3.setUtcTime(new TimestampedValue<>(1111L, 2222L));
         {
-            PhoneTimeSuggestion one = builder1.build();
-            PhoneTimeSuggestion three = builder3.build();
+            TelephonyTimeSuggestion one = builder1.build();
+            TelephonyTimeSuggestion three = builder3.build();
             assertNotEquals(one, three);
             assertNotEquals(three, one);
         }
@@ -72,15 +73,15 @@
         builder1.addDebugInfo("Debug info 1");
         builder2.addDebugInfo("Debug info 2");
         {
-            PhoneTimeSuggestion one = builder1.build();
-            PhoneTimeSuggestion two = builder2.build();
+            TelephonyTimeSuggestion one = builder1.build();
+            TelephonyTimeSuggestion two = builder2.build();
             assertEquals(one, two);
         }
     }
 
     @Test
     public void testParcelable() {
-        PhoneTimeSuggestion.Builder builder = new PhoneTimeSuggestion.Builder(SLOT_INDEX);
+        TelephonyTimeSuggestion.Builder builder = new TelephonyTimeSuggestion.Builder(SLOT_INDEX);
         assertRoundTripParcelable(builder.build());
 
         builder.setUtcTime(new TimestampedValue<>(1111L, 2222L));
@@ -88,9 +89,9 @@
 
         // DebugInfo should also be stored (but is not checked by equals()
         {
-            PhoneTimeSuggestion suggestion1 = builder.build();
+            TelephonyTimeSuggestion suggestion1 = builder.build();
             builder.addDebugInfo("This is debug info");
-            PhoneTimeSuggestion rtSuggestion1 = roundTripParcelable(suggestion1);
+            TelephonyTimeSuggestion rtSuggestion1 = roundTripParcelable(suggestion1);
             assertEquals(suggestion1.getDebugInfo(), rtSuggestion1.getDebugInfo());
         }
     }
diff --git a/core/tests/coretests/src/android/app/timezonedetector/PhoneTimeZoneSuggestionTest.java b/core/tests/coretests/src/android/app/timezonedetector/PhoneTimeZoneSuggestionTest.java
deleted file mode 100644
index 384dbf9..0000000
--- a/core/tests/coretests/src/android/app/timezonedetector/PhoneTimeZoneSuggestionTest.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright 2019 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.timezonedetector;
-
-import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable;
-import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcelable;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertTrue;
-
-import org.junit.Test;
-
-public class PhoneTimeZoneSuggestionTest {
-    private static final int SLOT_INDEX = 99999;
-
-    @Test
-    public void testEquals() {
-        PhoneTimeZoneSuggestion.Builder builder1 = new PhoneTimeZoneSuggestion.Builder(SLOT_INDEX);
-        {
-            PhoneTimeZoneSuggestion one = builder1.build();
-            assertEquals(one, one);
-        }
-
-        PhoneTimeZoneSuggestion.Builder builder2 = new PhoneTimeZoneSuggestion.Builder(SLOT_INDEX);
-        {
-            PhoneTimeZoneSuggestion one = builder1.build();
-            PhoneTimeZoneSuggestion two = builder2.build();
-            assertEquals(one, two);
-            assertEquals(two, one);
-        }
-
-        PhoneTimeZoneSuggestion.Builder builder3 =
-                new PhoneTimeZoneSuggestion.Builder(SLOT_INDEX + 1);
-        {
-            PhoneTimeZoneSuggestion one = builder1.build();
-            PhoneTimeZoneSuggestion three = builder3.build();
-            assertNotEquals(one, three);
-            assertNotEquals(three, one);
-        }
-
-        builder1.setZoneId("Europe/London");
-        builder1.setMatchType(PhoneTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_ONLY);
-        builder1.setQuality(PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE);
-        {
-            PhoneTimeZoneSuggestion one = builder1.build();
-            PhoneTimeZoneSuggestion two = builder2.build();
-            assertNotEquals(one, two);
-        }
-
-        builder2.setZoneId("Europe/Paris");
-        builder2.setMatchType(PhoneTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_ONLY);
-        builder2.setQuality(PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE);
-        {
-            PhoneTimeZoneSuggestion one = builder1.build();
-            PhoneTimeZoneSuggestion two = builder2.build();
-            assertNotEquals(one, two);
-        }
-
-        builder1.setZoneId("Europe/Paris");
-        {
-            PhoneTimeZoneSuggestion one = builder1.build();
-            PhoneTimeZoneSuggestion two = builder2.build();
-            assertEquals(one, two);
-        }
-
-        builder1.setMatchType(PhoneTimeZoneSuggestion.MATCH_TYPE_EMULATOR_ZONE_ID);
-        builder2.setMatchType(PhoneTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_ONLY);
-        {
-            PhoneTimeZoneSuggestion one = builder1.build();
-            PhoneTimeZoneSuggestion two = builder2.build();
-            assertNotEquals(one, two);
-        }
-
-        builder1.setMatchType(PhoneTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_ONLY);
-        {
-            PhoneTimeZoneSuggestion one = builder1.build();
-            PhoneTimeZoneSuggestion two = builder2.build();
-            assertEquals(one, two);
-        }
-
-        builder1.setQuality(PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE);
-        builder2.setQuality(PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS);
-        {
-            PhoneTimeZoneSuggestion one = builder1.build();
-            PhoneTimeZoneSuggestion two = builder2.build();
-            assertNotEquals(one, two);
-        }
-
-        builder1.setQuality(PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS);
-        {
-            PhoneTimeZoneSuggestion one = builder1.build();
-            PhoneTimeZoneSuggestion two = builder2.build();
-            assertEquals(one, two);
-        }
-
-        // DebugInfo must not be considered in equals().
-        {
-            PhoneTimeZoneSuggestion one = builder1.build();
-            PhoneTimeZoneSuggestion two = builder2.build();
-            one.addDebugInfo("Debug info 1");
-            two.addDebugInfo("Debug info 2");
-            assertEquals(one, two);
-        }
-    }
-
-    @Test(expected = RuntimeException.class)
-    public void testBuilderValidates_emptyZone_badMatchType() {
-        PhoneTimeZoneSuggestion.Builder builder = new PhoneTimeZoneSuggestion.Builder(SLOT_INDEX);
-        // No zone ID, so match type should be left unset.
-        builder.setMatchType(PhoneTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET);
-        builder.build();
-    }
-
-    @Test(expected = RuntimeException.class)
-    public void testBuilderValidates_zoneSet_badMatchType() {
-        PhoneTimeZoneSuggestion.Builder builder = new PhoneTimeZoneSuggestion.Builder(SLOT_INDEX);
-        builder.setZoneId("Europe/London");
-        builder.setQuality(PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE);
-        builder.build();
-    }
-
-    @Test
-    public void testParcelable() {
-        PhoneTimeZoneSuggestion.Builder builder = new PhoneTimeZoneSuggestion.Builder(SLOT_INDEX);
-        assertRoundTripParcelable(builder.build());
-
-        builder.setZoneId("Europe/London");
-        builder.setMatchType(PhoneTimeZoneSuggestion.MATCH_TYPE_EMULATOR_ZONE_ID);
-        builder.setQuality(PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE);
-        PhoneTimeZoneSuggestion suggestion1 = builder.build();
-        assertRoundTripParcelable(suggestion1);
-
-        // DebugInfo should also be stored (but is not checked by equals()
-        String debugString = "This is debug info";
-        suggestion1.addDebugInfo(debugString);
-        PhoneTimeZoneSuggestion suggestion1_2 = roundTripParcelable(suggestion1);
-        assertEquals(suggestion1, suggestion1_2);
-        assertTrue(suggestion1_2.getDebugInfo().contains(debugString));
-    }
-}
diff --git a/core/tests/coretests/src/android/app/timezonedetector/TelephonyTimeZoneSuggestionTest.java b/core/tests/coretests/src/android/app/timezonedetector/TelephonyTimeZoneSuggestionTest.java
new file mode 100644
index 0000000..59d55b7
--- /dev/null
+++ b/core/tests/coretests/src/android/app/timezonedetector/TelephonyTimeZoneSuggestionTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2019 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.timezonedetector;
+
+import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable;
+import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcelable;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class TelephonyTimeZoneSuggestionTest {
+    private static final int SLOT_INDEX = 99999;
+
+    @Test
+    public void testEquals() {
+        TelephonyTimeZoneSuggestion.Builder builder1 =
+                new TelephonyTimeZoneSuggestion.Builder(SLOT_INDEX);
+        {
+            TelephonyTimeZoneSuggestion one = builder1.build();
+            assertEquals(one, one);
+        }
+
+        TelephonyTimeZoneSuggestion.Builder builder2 =
+                new TelephonyTimeZoneSuggestion.Builder(SLOT_INDEX);
+        {
+            TelephonyTimeZoneSuggestion one = builder1.build();
+            TelephonyTimeZoneSuggestion two = builder2.build();
+            assertEquals(one, two);
+            assertEquals(two, one);
+        }
+
+        TelephonyTimeZoneSuggestion.Builder builder3 =
+                new TelephonyTimeZoneSuggestion.Builder(SLOT_INDEX + 1);
+        {
+            TelephonyTimeZoneSuggestion one = builder1.build();
+            TelephonyTimeZoneSuggestion three = builder3.build();
+            assertNotEquals(one, three);
+            assertNotEquals(three, one);
+        }
+
+        builder1.setZoneId("Europe/London");
+        builder1.setMatchType(TelephonyTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_ONLY);
+        builder1.setQuality(TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE);
+        {
+            TelephonyTimeZoneSuggestion one = builder1.build();
+            TelephonyTimeZoneSuggestion two = builder2.build();
+            assertNotEquals(one, two);
+        }
+
+        builder2.setZoneId("Europe/Paris");
+        builder2.setMatchType(TelephonyTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_ONLY);
+        builder2.setQuality(TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE);
+        {
+            TelephonyTimeZoneSuggestion one = builder1.build();
+            TelephonyTimeZoneSuggestion two = builder2.build();
+            assertNotEquals(one, two);
+        }
+
+        builder1.setZoneId("Europe/Paris");
+        {
+            TelephonyTimeZoneSuggestion one = builder1.build();
+            TelephonyTimeZoneSuggestion two = builder2.build();
+            assertEquals(one, two);
+        }
+
+        builder1.setMatchType(TelephonyTimeZoneSuggestion.MATCH_TYPE_EMULATOR_ZONE_ID);
+        builder2.setMatchType(TelephonyTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_ONLY);
+        {
+            TelephonyTimeZoneSuggestion one = builder1.build();
+            TelephonyTimeZoneSuggestion two = builder2.build();
+            assertNotEquals(one, two);
+        }
+
+        builder1.setMatchType(TelephonyTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_ONLY);
+        {
+            TelephonyTimeZoneSuggestion one = builder1.build();
+            TelephonyTimeZoneSuggestion two = builder2.build();
+            assertEquals(one, two);
+        }
+
+        builder1.setQuality(TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE);
+        builder2.setQuality(
+                TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS);
+        {
+            TelephonyTimeZoneSuggestion one = builder1.build();
+            TelephonyTimeZoneSuggestion two = builder2.build();
+            assertNotEquals(one, two);
+        }
+
+        builder1.setQuality(
+                TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS);
+        {
+            TelephonyTimeZoneSuggestion one = builder1.build();
+            TelephonyTimeZoneSuggestion two = builder2.build();
+            assertEquals(one, two);
+        }
+
+        // DebugInfo must not be considered in equals().
+        {
+            TelephonyTimeZoneSuggestion one = builder1.build();
+            TelephonyTimeZoneSuggestion two = builder2.build();
+            one.addDebugInfo("Debug info 1");
+            two.addDebugInfo("Debug info 2");
+            assertEquals(one, two);
+        }
+    }
+
+    @Test(expected = RuntimeException.class)
+    public void testBuilderValidates_emptyZone_badMatchType() {
+        TelephonyTimeZoneSuggestion.Builder builder =
+                new TelephonyTimeZoneSuggestion.Builder(SLOT_INDEX);
+        // No zone ID, so match type should be left unset.
+        builder.setMatchType(TelephonyTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET);
+        builder.build();
+    }
+
+    @Test(expected = RuntimeException.class)
+    public void testBuilderValidates_zoneSet_badMatchType() {
+        TelephonyTimeZoneSuggestion.Builder builder =
+                new TelephonyTimeZoneSuggestion.Builder(SLOT_INDEX);
+        builder.setZoneId("Europe/London");
+        builder.setQuality(TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE);
+        builder.build();
+    }
+
+    @Test
+    public void testParcelable() {
+        TelephonyTimeZoneSuggestion.Builder builder =
+                new TelephonyTimeZoneSuggestion.Builder(SLOT_INDEX);
+        assertRoundTripParcelable(builder.build());
+
+        builder.setZoneId("Europe/London");
+        builder.setMatchType(TelephonyTimeZoneSuggestion.MATCH_TYPE_EMULATOR_ZONE_ID);
+        builder.setQuality(TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE);
+        TelephonyTimeZoneSuggestion suggestion1 = builder.build();
+        assertRoundTripParcelable(suggestion1);
+
+        // DebugInfo should also be stored (but is not checked by equals()
+        String debugString = "This is debug info";
+        suggestion1.addDebugInfo(debugString);
+        TelephonyTimeZoneSuggestion suggestion1_2 = roundTripParcelable(suggestion1);
+        assertEquals(suggestion1, suggestion1_2);
+        assertTrue(suggestion1_2.getDebugInfo().contains(debugString));
+    }
+}
diff --git a/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java b/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java
index bf78203..7733559 100644
--- a/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java
+++ b/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java
@@ -90,18 +90,18 @@
     }
 
     @Test
-    public void testValidAtomicFormula_stringValue_appCertificateAutoHashed() {
+    public void testValidAtomicFormula_stringValue_appCertificateIsNotAutoHashed() {
         String appCert = "cert";
         StringAtomicFormula stringAtomicFormula =
                 new StringAtomicFormula(AtomicFormula.APP_CERTIFICATE, appCert);
 
         assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.APP_CERTIFICATE);
-        assertThat(stringAtomicFormula.getValue()).doesNotMatch(appCert);
-        assertThat(stringAtomicFormula.getIsHashedValue()).isTrue();
+        assertThat(stringAtomicFormula.getValue()).matches(appCert);
+        assertThat(stringAtomicFormula.getIsHashedValue()).isFalse();
     }
 
     @Test
-    public void testValidAtomicFormula_stringValue_installerCertificateAutoHashed() {
+    public void testValidAtomicFormula_stringValue_installerCertificateIsNotAutoHashed() {
         String installerCert = "cert";
         StringAtomicFormula stringAtomicFormula =
                 new StringAtomicFormula(AtomicFormula.INSTALLER_CERTIFICATE,
@@ -109,8 +109,8 @@
 
         assertThat(stringAtomicFormula.getKey()).isEqualTo(
                 AtomicFormula.INSTALLER_CERTIFICATE);
-        assertThat(stringAtomicFormula.getValue()).doesNotMatch(installerCert);
-        assertThat(stringAtomicFormula.getIsHashedValue()).isTrue();
+        assertThat(stringAtomicFormula.getValue()).matches(installerCert);
+        assertThat(stringAtomicFormula.getIsHashedValue()).isFalse();
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java b/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java
index c180602..dc03167 100644
--- a/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java
+++ b/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java
@@ -40,7 +40,7 @@
 
         assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.PACKAGE_NAME);
         assertThat(stringAtomicFormula.getValue()).isEqualTo(packageName);
-        assertThat(stringAtomicFormula.getIsHashedValue()).isEqualTo(false);
+        assertThat(stringAtomicFormula.getIsHashedValue()).isFalse();
     }
 
     @Test
@@ -53,8 +53,8 @@
                 (AtomicFormula.StringAtomicFormula) formula;
 
         assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.APP_CERTIFICATE);
-        assertThat(stringAtomicFormula.getValue()).doesNotMatch(appCertificate);
-        assertThat(stringAtomicFormula.getIsHashedValue()).isEqualTo(true);
+        assertThat(stringAtomicFormula.getValue()).matches(appCertificate);
+        assertThat(stringAtomicFormula.getIsHashedValue()).isFalse();
     }
 
     @Test
@@ -68,7 +68,7 @@
 
         assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.INSTALLER_NAME);
         assertThat(stringAtomicFormula.getValue()).isEqualTo(installerName);
-        assertThat(stringAtomicFormula.getIsHashedValue()).isEqualTo(false);
+        assertThat(stringAtomicFormula.getIsHashedValue()).isFalse();
     }
 
     @Test
@@ -81,8 +81,8 @@
                 (AtomicFormula.StringAtomicFormula) formula;
 
         assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.INSTALLER_CERTIFICATE);
-        assertThat(stringAtomicFormula.getValue()).doesNotMatch(installerCertificate);
-        assertThat(stringAtomicFormula.getIsHashedValue()).isEqualTo(true);
+        assertThat(stringAtomicFormula.getValue()).matches(installerCertificate);
+        assertThat(stringAtomicFormula.getIsHashedValue()).isFalse();
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/os/BuildTest.java b/core/tests/coretests/src/android/os/BuildTest.java
index decc768..2295eb9 100644
--- a/core/tests/coretests/src/android/os/BuildTest.java
+++ b/core/tests/coretests/src/android/os/BuildTest.java
@@ -60,7 +60,7 @@
         assertNotEmpty("BRAND", Build.BRAND);
         assertNotEmpty("MODEL", Build.MODEL);
         assertNotEmpty("VERSION.INCREMENTAL", Build.VERSION.INCREMENTAL);
-        assertNotEmpty("VERSION.RELEASE", Build.VERSION.RELEASE);
+        assertNotEmpty("VERSION.RELEASE", Build.VERSION.RELEASE_OR_CODENAME);
         assertNotEmpty("TYPE", Build.TYPE);
         Assert.assertNotNull("TAGS", Build.TAGS); // TAGS is allowed to be empty.
         assertNotEmpty("FINGERPRINT", Build.FINGERPRINT);
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index df6b906..ce71beb 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -38,6 +38,7 @@
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
@@ -355,6 +356,7 @@
         // enable the work tab feature flag
         ResolverActivity.ENABLE_TABBED_VIEW = true;
 
+        markWorkProfileUserAvailable();
         Intent sendIntent = createSendTextIntent();
         List<ResolvedComponentInfo> resolvedComponentInfos =
                 createResolvedComponentsForTestWithOtherProfile(2);
@@ -1209,17 +1211,24 @@
         // enable the work tab feature flag
         ResolverActivity.ENABLE_TABBED_VIEW = true;
         int personalProfileTargets = 3;
+        int otherProfileTargets = 1;
         List<ResolvedComponentInfo> personalResolvedComponentInfos =
-                createResolvedComponentsForTest(personalProfileTargets);
+                createResolvedComponentsForTestWithOtherProfile(
+                        personalProfileTargets + otherProfileTargets, /* userID */ 10);
         int workProfileTargets = 4;
         List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(
                 workProfileTargets);
         when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
                 Mockito.anyBoolean(),
-                Mockito.isA(List.class))).thenReturn(personalResolvedComponentInfos);
+                Mockito.isA(List.class)))
+                .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
         when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
                 Mockito.anyBoolean(),
-                Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
+                Mockito.isA(List.class))).thenReturn(new ArrayList<>(workResolvedComponentInfos));
+        when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class),
+                eq(UserHandle.SYSTEM))).thenReturn(new ArrayList<>(personalResolvedComponentInfos));
         Intent sendIntent = createSendTextIntent();
         sendIntent.setType("TestType");
         markWorkProfileUserAvailable();
@@ -1229,8 +1238,6 @@
         waitForIdle();
 
         assertThat(activity.getCurrentUserHandle().getIdentifier(), is(0));
-        // The work list adapter must only be filled when we open the work tab
-        assertThat(activity.getWorkListAdapter().getCount(), is(0));
         onView(withText(R.string.resolver_work_tab)).perform(click());
         assertThat(activity.getCurrentUserHandle().getIdentifier(), is(10));
         assertThat(activity.getPersonalListAdapter().getCount(), is(personalProfileTargets));
@@ -1243,11 +1250,22 @@
         ResolverActivity.ENABLE_TABBED_VIEW = true;
         markWorkProfileUserAvailable();
         int workProfileTargets = 4;
+        List<ResolvedComponentInfo> personalResolvedComponentInfos =
+                createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
         List<ResolvedComponentInfo> workResolvedComponentInfos =
                 createResolvedComponentsForTest(workProfileTargets);
+        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class)))
+                .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
         when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
                 Mockito.anyBoolean(),
                 Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
+        when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class),
+                eq(UserHandle.SYSTEM)))
+                .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
         Intent sendIntent = createSendTextIntent();
         sendIntent.setType("TestType");
 
@@ -1357,6 +1375,20 @@
         return infoList;
     }
 
+    private List<ResolvedComponentInfo> createResolvedComponentsForTestWithOtherProfile(
+            int numberOfResults, int userId) {
+        List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
+        for (int i = 0; i < numberOfResults; i++) {
+            if (i == 0) {
+                infoList.add(
+                        ResolverDataProvider.createResolvedComponentInfoWithOtherId(i, userId));
+            } else {
+                infoList.add(ResolverDataProvider.createResolvedComponentInfo(i));
+            }
+        }
+        return infoList;
+    }
+
     private List<ResolvedComponentInfo> createResolvedComponentsForTestWithUserId(
             int numberOfResults, int userId) {
         List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
index eee62bb..a68b5908 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -16,10 +16,15 @@
 
 package com.android.internal.app;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import android.annotation.Nullable;
+import android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppPredictionManager;
+import android.app.prediction.AppPredictor;
 import android.app.usage.UsageStatsManager;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -40,6 +45,8 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
+import org.mockito.Mockito;
+
 import java.util.function.Function;
 
 public class ChooserWrapperActivity extends ChooserActivity {
@@ -173,6 +180,12 @@
         return mMultiProfilePagerAdapter.getCurrentUserHandle();
     }
 
+    @Override
+    public Context createContextAsUser(UserHandle user, int flags) {
+        // return the current context as a work profile doesn't really exist in these tests
+        return getApplicationContext();
+    }
+
     /**
      * We cannot directly mock the activity created since instrumentation creates it.
      * <p>
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index 911490f..5f4194a 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -225,6 +225,7 @@
         // enable the work tab feature flag
         ResolverActivity.ENABLE_TABBED_VIEW = true;
 
+        markWorkProfileUserAvailable();
         Intent sendIntent = createSendImageIntent();
         List<ResolvedComponentInfo> resolvedComponentInfos =
                 createResolvedComponentsForTestWithOtherProfile(2);
@@ -246,7 +247,6 @@
             chosen[0] = targetInfo.getResolveInfo();
             return true;
         };
-
         // Make a stable copy of the components as the original list may be modified
         List<ResolvedComponentInfo> stableCopy =
                 createResolvedComponentsForTestWithOtherProfile(2);
@@ -443,7 +443,7 @@
         // enable the work tab feature flag
         ResolverActivity.ENABLE_TABBED_VIEW = true;
         List<ResolvedComponentInfo> personalResolvedComponentInfos =
-                createResolvedComponentsForTest(3);
+                createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
         List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
         when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
                 Mockito.anyBoolean(),
@@ -451,6 +451,11 @@
         when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
                 Mockito.anyBoolean(),
                 Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
+        when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class),
+                eq(UserHandle.SYSTEM)))
+                .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
         Intent sendIntent = createSendImageIntent();
         markWorkProfileUserAvailable();
 
@@ -478,17 +483,20 @@
                 Mockito.anyBoolean(),
                 Mockito.anyBoolean(),
                 Mockito.isA(List.class),
-                eq(sOverrides.workProfileUserHandle))).thenReturn(new ArrayList<>(workResolvedComponentInfos));
+                eq(sOverrides.workProfileUserHandle)))
+                .thenReturn(new ArrayList<>(workResolvedComponentInfos));
         when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
                 Mockito.anyBoolean(),
                 Mockito.isA(List.class),
-                eq(sOverrides.workProfileUserHandle))).thenReturn(new ArrayList<>(workResolvedComponentInfos));
+                eq(sOverrides.workProfileUserHandle)))
+                .thenReturn(new ArrayList<>(workResolvedComponentInfos));
         when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
                 Mockito.anyBoolean(),
                 Mockito.isA(List.class))).thenReturn(new ArrayList<>(workResolvedComponentInfos));
         when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
                 Mockito.anyBoolean(),
-                Mockito.isA(List.class))).thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+                Mockito.isA(List.class)))
+                .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
         when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
                 Mockito.anyBoolean(),
                 Mockito.isA(List.class),
@@ -502,7 +510,7 @@
         onView(withText(R.string.resolver_work_tab)).perform(click());
 
         assertThat(activity.getCurrentUserHandle().getIdentifier(), is(10));
-        assertThat(activity.getPersonalListAdapter().getCount(), is(3));
+        assertThat(activity.getPersonalListAdapter().getCount(), is(2));
     }
 
     @Test
@@ -511,14 +519,20 @@
         ResolverActivity.ENABLE_TABBED_VIEW = true;
         markWorkProfileUserAvailable();
         List<ResolvedComponentInfo> personalResolvedComponentInfos =
-                createResolvedComponentsForTest(3);
+                createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
         List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
         when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
                 Mockito.anyBoolean(),
-                Mockito.isA(List.class))).thenReturn(personalResolvedComponentInfos);
+                Mockito.isA(List.class)))
+                .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
         when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
                 Mockito.anyBoolean(),
                 Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
+        when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class),
+                eq(UserHandle.SYSTEM)))
+                .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
         Intent sendIntent = createSendImageIntent();
 
         final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
@@ -536,14 +550,20 @@
         ResolverActivity.ENABLE_TABBED_VIEW = true;
         markWorkProfileUserAvailable();
         List<ResolvedComponentInfo> personalResolvedComponentInfos =
-                createResolvedComponentsForTest(3);
+                createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
         List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
         when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
                 Mockito.anyBoolean(),
-                Mockito.isA(List.class))).thenReturn(personalResolvedComponentInfos);
+                Mockito.isA(List.class)))
+                .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
         when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
                 Mockito.anyBoolean(),
                 Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
+        when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class),
+                eq(UserHandle.SYSTEM)))
+                .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
         Intent sendIntent = createSendImageIntent();
         ResolveInfo[] chosen = new ResolveInfo[1];
         sOverrides.onSafelyStartCallback = targetInfo -> {
@@ -587,17 +607,20 @@
                 Mockito.anyBoolean(),
                 Mockito.anyBoolean(),
                 Mockito.isA(List.class),
-                eq(sOverrides.workProfileUserHandle))).thenReturn(new ArrayList<>(workResolvedComponentInfos));
+                eq(sOverrides.workProfileUserHandle)))
+                .thenReturn(new ArrayList<>(workResolvedComponentInfos));
         when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
                 Mockito.anyBoolean(),
                 Mockito.isA(List.class),
-                eq(sOverrides.workProfileUserHandle))).thenReturn(new ArrayList<>(workResolvedComponentInfos));
+                eq(sOverrides.workProfileUserHandle)))
+                .thenReturn(new ArrayList<>(workResolvedComponentInfos));
         when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
                 Mockito.anyBoolean(),
                 Mockito.isA(List.class))).thenReturn(new ArrayList<>(workResolvedComponentInfos));
         when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
                 Mockito.anyBoolean(),
-                Mockito.isA(List.class))).thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+                Mockito.isA(List.class)))
+                .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
         when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
                 Mockito.anyBoolean(),
                 Mockito.isA(List.class),
@@ -678,6 +701,20 @@
         return infoList;
     }
 
+    private List<ResolvedComponentInfo> createResolvedComponentsForTestWithOtherProfile(
+            int numberOfResults, int userId) {
+        List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
+        for (int i = 0; i < numberOfResults; i++) {
+            if (i == 0) {
+                infoList.add(
+                        ResolverDataProvider.createResolvedComponentInfoWithOtherId(i, userId));
+            } else {
+                infoList.add(ResolverDataProvider.createResolvedComponentInfo(i));
+            }
+        }
+        return infoList;
+    }
+
     private void waitForIdle() {
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
     }
diff --git a/data/etc/com.android.settings.xml b/data/etc/com.android.settings.xml
index a200a51..fe1182e 100644
--- a/data/etc/com.android.settings.xml
+++ b/data/etc/com.android.settings.xml
@@ -26,6 +26,7 @@
         <permission name="android.permission.DELETE_PACKAGES"/>
         <permission name="android.permission.FORCE_STOP_PACKAGES"/>
         <permission name="android.permission.LOCAL_MAC_ADDRESS"/>
+        <permission name="android.permission.LOG_COMPAT_CHANGE" />
         <permission name="android.permission.MANAGE_DEBUGGING"/>
         <permission name="android.permission.MANAGE_DEVICE_ADMINS"/>
         <permission name="android.permission.MANAGE_FINGERPRINT"/>
@@ -37,8 +38,10 @@
         <permission name="android.permission.MODIFY_PHONE_STATE"/>
         <permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
         <permission name="android.permission.MOVE_PACKAGE"/>
+        <permission name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG" />
         <permission name="android.permission.OVERRIDE_WIFI_CONFIG"/>
         <permission name="android.permission.PACKAGE_USAGE_STATS"/>
+        <permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
         <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
         <permission name="android.permission.READ_SEARCH_INDEXABLES"/>
         <permission name="android.permission.REBOOT"/>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index b5eba09..f83fb3f 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -169,7 +169,7 @@
         <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
         <permission name="android.permission.STATUS_BAR"/>
         <permission name="android.permission.STOP_APP_SWITCHES"/>
-        <permission name="android.permission.SUGGEST_PHONE_TIME_AND_ZONE"/>
+        <permission name="android.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE"/>
         <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
         <permission name="android.permission.UPDATE_DEVICE_STATS"/>
         <permission name="android.permission.UPDATE_LOCK"/>
@@ -366,6 +366,10 @@
         <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
         <!-- Permission required for Tethering CTS tests. -->
         <permission name="android.permission.TETHER_PRIVILEGED"/>
+        <!-- Permissions required for ganting and logging -->
+        <permission name="android.permission.LOG_COMPAT_CHANGE" />
+        <permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
+        <permission name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG" />
         <!-- Permissions required to test ambient display. -->
         <permission name="android.permission.READ_DREAM_STATE" />
         <permission name="android.permission.WRITE_DREAM_STATE" />
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 0b5e005..aa8bac9 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -174,7 +174,7 @@
         // Enable frame stats after the surface has been bound to the appropriate graphics API.
         // Order is important when new and old surfaces are the same, because old surface has
         // its frame stats disabled automatically.
-        mNativeSurface->enableFrameTimestamps(true);
+        native_window_enable_frame_timestamps(mNativeSurface->getNativeWindow(), true);
     } else {
         mRenderThread.removeFrameCallback(this);
         mGenerationID++;
@@ -556,8 +556,9 @@
         FrameInfo* forthBehind = mLast4FrameInfos.front().first;
         int64_t composedFrameId = mLast4FrameInfos.front().second;
         nsecs_t acquireTime = -1;
-        mNativeSurface->getFrameTimestamps(composedFrameId, nullptr, &acquireTime, nullptr, nullptr,
-            nullptr, nullptr, nullptr, nullptr, nullptr);
+        native_window_get_frame_timestamps(mNativeSurface->getNativeWindow(), composedFrameId,
+                                           nullptr, &acquireTime, nullptr, nullptr, nullptr,
+                                           nullptr, nullptr, nullptr, nullptr);
         // Ignore default -1, NATIVE_WINDOW_TIMESTAMP_INVALID and NATIVE_WINDOW_TIMESTAMP_PENDING
         forthBehind->set(FrameInfoIndex::GpuCompleted) = acquireTime > 0 ? acquireTime : -1;
         mJankTracker.finishGpuDraw(*forthBehind);
diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h
index da5097c..e3cd8c0 100644
--- a/libs/hwui/renderthread/ReliableSurface.h
+++ b/libs/hwui/renderthread/ReliableSurface.h
@@ -49,21 +49,6 @@
         return ret;
     }
 
-    status_t getFrameTimestamps(uint64_t frameNumber,
-            nsecs_t* outRequestedPresentTime, nsecs_t* outAcquireTime,
-            nsecs_t* outLatchTime, nsecs_t* outFirstRefreshStartTime,
-            nsecs_t* outLastRefreshStartTime, nsecs_t* outGlCompositionDoneTime,
-            nsecs_t* outDisplayPresentTime, nsecs_t* outDequeueReadyTime,
-            nsecs_t* outReleaseTime) {
-        return mSurface->getFrameTimestamps(frameNumber, outRequestedPresentTime, outAcquireTime,
-            outLatchTime, outFirstRefreshStartTime, outLastRefreshStartTime,
-            outGlCompositionDoneTime, outDisplayPresentTime, outDequeueReadyTime, outReleaseTime);
-    }
-
-    void enableFrameTimestamps(bool enable) {
-        return mSurface->enableFrameTimestamps(enable);
-    }
-
 private:
     sp<Surface> mSurface;
 
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 6a89b71..1d679c7 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -223,6 +223,11 @@
     <!-- permissions required for CTS test - PhoneStateListenerTest -->
     <uses-permission android:name="android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH" />
 
+    <!-- Permissions required for ganting and logging -->
+    <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/>
+    <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/>
+    <uses-permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG"/>
+
     <!-- Permission required for CTS test - UiModeManagerTest -->
     <uses-permission android:name="android.permission.ENTER_CAR_MODE_PRIORITIZED"/>
 
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 139a8c3..1fe967b 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -645,7 +645,6 @@
         <activity android:name=".controls.management.ControlsProviderSelectorActivity"
                   android:label="Controls Providers"
                   android:theme="@style/Theme.ControlsManagement"
-                  android:exported="true"
                   android:showForAllUsers="true"
                   android:excludeFromRecents="true"
                   android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
diff --git a/packages/SystemUI/res/drawable/ic_device_unknown_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_device_unknown_gm2_24px.xml
new file mode 100644
index 0000000..24e0635
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_unknown_gm2_24px.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:fillColor="#FF000000"
+      android:pathData="M12,2l-5.5,9h11L12,2zM12,5.84L13.93,9h-3.87L12,5.84zM17.5,13c-2.49,0 -4.5,2.01 -4.5,4.5s2.01,4.5 4.5,4.5 4.5,-2.01 4.5,-4.5 -2.01,-4.5 -4.5,-4.5zM17.5,20c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5zM3,21.5h8v-8L3,13.5v8zM5,15.5h4v4L5,19.5v-4z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_touch.xml b/packages/SystemUI/res/drawable/ic_touch.xml
new file mode 100644
index 0000000..4f6698d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_touch.xml
@@ -0,0 +1,26 @@
+<!--
+Copyright (C) 2020 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.
+-->
+<!-- maybe need android:fillType="evenOdd" -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9,7.5V11.24C7.79,10.43 7,9.06 7,7.5C7,5.01 9.01,3 11.5,3C13.99,3 16,5.01 16,7.5C16,9.06 15.21,10.43 14,11.24V7.5C14,6.12 12.88,5 11.5,5C10.12,5 9,6.12 9,7.5ZM14.3,13.61L18.84,15.87C19.37,16.09 19.75,16.63 19.75,17.25C19.75,17.31 19.74,17.38 19.73,17.45L18.98,22.72C18.87,23.45 18.29,24 17.54,24H10.75C10.34,24 9.96,23.83 9.69,23.56L4.75,18.62L5.54,17.82C5.74,17.62 6.02,17.49 6.33,17.49C6.39,17.49 6.4411,17.4989 6.4922,17.5078C6.5178,17.5122 6.5433,17.5167 6.57,17.52L10,18.24V7.5C10,6.67 10.67,6 11.5,6C12.33,6 13,6.67 13,7.5V13.5H13.76C13.95,13.5 14.13,13.54 14.3,13.61Z"
+    />
+</vector>
diff --git a/packages/SystemUI/res/layout/screen_record_dialog.xml b/packages/SystemUI/res/layout/screen_record_dialog.xml
new file mode 100644
index 0000000..df576d8
--- /dev/null
+++ b/packages/SystemUI/res/layout/screen_record_dialog.xml
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2020 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.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:background="@drawable/rounded_bg_full">
+
+    <!-- Header -->
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        android:gravity="center"
+        android:padding="@dimen/screenrecord_dialog_padding">
+        <ImageView
+            android:layout_width="@dimen/screenrecord_logo_size"
+            android:layout_height="@dimen/screenrecord_logo_size"
+            android:src="@drawable/ic_screenrecord"
+            android:tint="@color/GM2_red_500"
+            android:layout_marginBottom="@dimen/screenrecord_dialog_padding"/>
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceLarge"
+            android:text="@string/screenrecord_start_label"/>
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/screenrecord_description"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:paddingTop="@dimen/screenrecord_dialog_padding"
+            android:paddingBottom="@dimen/screenrecord_dialog_padding"/>
+
+        <!-- Options -->
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+            <ImageView
+                android:layout_width="@dimen/screenrecord_logo_size"
+                android:layout_height="@dimen/screenrecord_logo_size"
+                android:src="@drawable/ic_mic_26dp"
+                android:tint="@color/GM2_grey_700"
+                android:layout_gravity="center"
+                android:layout_weight="0"
+                android:layout_marginRight="@dimen/screenrecord_dialog_padding"/>
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:orientation="vertical"
+                android:layout_weight="1">
+                <TextView
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:layout_gravity="center_vertical"
+                    android:text="@string/screenrecord_audio_label"
+                    android:textColor="?android:attr/textColorPrimary"
+                    android:textAppearance="?android:attr/textAppearanceMedium"/>
+                <TextView
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:id="@+id/audio_type"
+                    android:text="@string/screenrecord_mic_label"
+                    android:textAppearance="?android:attr/textAppearanceSmall"/>
+            </LinearLayout>
+            <Switch
+                android:layout_width="wrap_content"
+                android:layout_height="48dp"
+                android:layout_weight="0"
+                android:id="@+id/screenrecord_audio_switch"/>
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+            <ImageView
+                android:layout_width="@dimen/screenrecord_logo_size"
+                android:layout_height="@dimen/screenrecord_logo_size"
+                android:src="@drawable/ic_touch"
+                android:tint="@color/GM2_grey_700"
+                android:layout_gravity="center"
+                android:layout_marginRight="@dimen/screenrecord_dialog_padding"/>
+            <Switch
+                android:layout_width="match_parent"
+                android:layout_height="48dp"
+                android:id="@+id/screenrecord_taps_switch"
+                android:text="@string/screenrecord_taps_label"
+                android:textColor="?android:attr/textColorPrimary"
+                android:textAppearance="?android:attr/textAppearanceMedium"/>
+        </LinearLayout>
+    </LinearLayout>
+
+    <!-- hr -->
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:background="@color/GM2_grey_300"/>
+
+    <!-- Buttons -->
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:padding="@dimen/screenrecord_dialog_padding">
+        <Button
+            android:id="@+id/button_cancel"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_weight="0"
+            android:layout_gravity="start"
+            android:text="@string/cancel"
+            style="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"/>
+        <Space
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1"/>
+        <Button
+            android:id="@+id/button_start"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_weight="0"
+            android:layout_gravity="end"
+            android:text="@string/screenrecord_start"
+            style="@android:style/Widget.DeviceDefault.Button.Colored"/>
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 5a1d1e2..c4fa4e5 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1209,5 +1209,7 @@
 
     <dimen name="controls_card_margin">2dp</dimen>
 
-
+    <!-- Screen Record -->
+    <dimen name="screenrecord_dialog_padding">18dp</dimen>
+    <dimen name="screenrecord_logo_size">24dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 07a926f..b85b51e 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -217,15 +217,31 @@
         your organization</string>
 
     <!-- Notification title displayed for screen recording [CHAR LIMIT=50]-->
-    <string name="screenrecord_name">Screen Recording</string>
+    <string name="screenrecord_name">Screen Recorder</string>
     <!-- Description of the screen recording notification channel [CHAR LIMIT=NONE]-->
     <string name="screenrecord_channel_description">Ongoing notification for a screen record session</string>
-    <!-- Label for the button to begin screen recording [CHAR LIMIT=NONE]-->
-    <string name="screenrecord_start_label">Start Recording</string>
-    <!-- Label for the checkbox to enable microphone input during screen recording [CHAR LIMIT=NONE]-->
-    <string name="screenrecord_mic_label">Record voiceover</string>
+    <!-- Title for the screen prompting the user to begin recording their screen [CHAR LIMIT=NONE]-->
+    <string name="screenrecord_start_label">Start Recording?</string>
+    <!-- Message reminding the user that sensitive information may be captured during a screen recording [CHAR_LIMIT=NONE]-->
+    <string name="screenrecord_description">While recording, Android System can capture any sensitive information that\u2019s visible on your screen or played on your device. This includes passwords, payment info, photos, messages, and audio.</string>
+    <!-- Label for a switch to enable recording audio [CHAR LIMIT=NONE]-->
+    <string name="screenrecord_audio_label">Record audio</string>
+    <!-- Label for the option to record audio from the device [CHAR LIMIT=NONE]-->
+    <string name="screenrecord_device_audio_label">Device audio</string>
+    <!-- Description of what audio may be captured from the device [CHAR LIMIT=NONE]-->
+    <string name="screenrecord_device_audio_description">Sound from your device, like music, calls, and ringtones</string>
+    <!-- Label for the option to enable microphone input during screen recording [CHAR LIMIT=NONE]-->
+    <string name="screenrecord_mic_label">Microphone</string>
+    <!-- Label for an option to record audio from both device and microphone [CHAR LIMIT=NONE]-->
+    <string name="screenrecord_device_audio_and_mic_label">Device audio and microphone</string>
+    <!-- Button to start a screen recording [CHAR LIMIT=50]-->
+    <string name="screenrecord_start">Start</string>
+    <!-- Notification text displayed when we are recording the screen [CHAR LIMIT=100]-->
+    <string name="screenrecord_ongoing_screen_only">Recording screen</string>
+    <!-- Notification text displayed when we are recording both the screen and audio [CHAR LIMIT=100]-->
+    <string name="screenrecord_ongoing_screen_and_audio">Recording screen and audio</string>
     <!-- Label for the checkbox to enable showing location of touches during screen recording [CHAR LIMIT=NONE]-->
-    <string name="screenrecord_taps_label">Show taps</string>
+    <string name="screenrecord_taps_label">Show touches on screen</string>
     <!-- Label for notification that the user can tap to stop and save the screen recording [CHAR LIMIT=NONE] -->
     <string name="screenrecord_stop_text">Tap to stop</string>
     <!-- Label for notification action to stop and save the screen recording [CHAR LIMIT=35] -->
@@ -250,6 +266,8 @@
     <string name="screenrecord_delete_error">Error deleting screen recording</string>
     <!-- A toast message shown when the screen recording cannot be started due to insufficient permissions [CHAR LIMIT=NONE] -->
     <string name="screenrecord_permission_error">Failed to get permissions</string>
+    <!-- A toast message shown when the screen recording cannot be started due to a generic error [CHAR LIMIT=NONE] -->
+    <string name="screenrecord_start_error">Error starting screen recording</string>
 
     <!-- Title for the USB function chooser in UsbPreferenceActivity. [CHAR LIMIT=30] -->
     <string name="usb_preference_title">USB file transfer options</string>
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index a6f1d84..7de1557 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -19,9 +19,12 @@
 import android.app.PendingIntent
 import android.content.BroadcastReceiver
 import android.content.ComponentName
+import android.content.ContentResolver
 import android.content.Context
 import android.content.Intent
 import android.content.IntentFilter
+import android.database.ContentObserver
+import android.net.Uri
 import android.os.Environment
 import android.os.UserHandle
 import android.provider.Settings
@@ -30,6 +33,7 @@
 import android.util.ArrayMap
 import android.util.Log
 import com.android.internal.annotations.GuardedBy
+import com.android.internal.annotations.VisibleForTesting
 import com.android.systemui.DumpController
 import com.android.systemui.Dumpable
 import com.android.systemui.broadcast.BroadcastDispatcher
@@ -53,15 +57,16 @@
     private val uiController: ControlsUiController,
     private val bindingController: ControlsBindingController,
     private val listingController: ControlsListingController,
-    broadcastDispatcher: BroadcastDispatcher,
+    private val broadcastDispatcher: BroadcastDispatcher,
     optionalWrapper: Optional<ControlsFavoritePersistenceWrapper>,
     dumpController: DumpController
 ) : Dumpable, ControlsController {
 
     companion object {
         private const val TAG = "ControlsControllerImpl"
-        const val CONTROLS_AVAILABLE = "systemui.controls_available"
-        const val USER_CHANGE_RETRY_DELAY = 500L // ms
+        internal const val CONTROLS_AVAILABLE = "systemui.controls_available"
+        internal val URI = Settings.Secure.getUriFor(CONTROLS_AVAILABLE)
+        private const val USER_CHANGE_RETRY_DELAY = 500L // ms
     }
 
     // Map of map: ComponentName -> (String -> ControlInfo).
@@ -69,9 +74,11 @@
     @GuardedBy("currentFavorites")
     private val currentFavorites = ArrayMap<ComponentName, MutableMap<String, ControlInfo>>()
 
-    private var userChanging = true
-    override var available = Settings.Secure.getInt(
-            context.contentResolver, CONTROLS_AVAILABLE, 0) != 0
+    private var userChanging: Boolean = true
+
+    private val contentResolver: ContentResolver
+        get() = context.contentResolver
+    override var available = Settings.Secure.getInt(contentResolver, CONTROLS_AVAILABLE, 0) != 0
         private set
 
     private var currentUser = context.user
@@ -95,8 +102,8 @@
         val fileName = Environment.buildPath(
                 userContext.filesDir, ControlsFavoritePersistenceWrapper.FILE_NAME)
         persistenceWrapper.changeFile(fileName)
-        available = Settings.Secure.getIntForUser(
-                context.contentResolver, CONTROLS_AVAILABLE, 0) != 0
+        available = Settings.Secure.getIntForUser(contentResolver, CONTROLS_AVAILABLE,
+                /* default */ 0, newUser.identifier) != 0
         synchronized(currentFavorites) {
             currentFavorites.clear()
         }
@@ -123,6 +130,25 @@
         }
     }
 
+    @VisibleForTesting
+    internal val settingObserver = object : ContentObserver(null) {
+        override fun onChange(selfChange: Boolean, uri: Uri, userId: Int) {
+            // Do not listen to changes in the middle of user change, those will be read by the
+            // user-switch receiver.
+            if (userChanging || userId != currentUserId) {
+                return
+            }
+            available = Settings.Secure.getIntForUser(contentResolver, CONTROLS_AVAILABLE,
+                    /* default */ 0, currentUserId) != 0
+            synchronized(currentFavorites) {
+                currentFavorites.clear()
+            }
+            if (available) {
+                loadFavorites()
+            }
+        }
+    }
+
     init {
         dumpController.registerDumpable(this)
         if (available) {
@@ -135,6 +161,7 @@
                 executor,
                 UserHandle.ALL
         )
+        contentResolver.registerContentObserver(URI, false, settingObserver, UserHandle.USER_ALL)
     }
 
     private fun confirmAvailability(): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index fad2d94..88b19b5 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -34,14 +34,17 @@
 import android.widget.TextView
 
 import com.android.systemui.controls.controller.ControlsController
+import com.android.systemui.util.concurrency.DelayableExecutor
 import com.android.systemui.R
 
 const val MIN_LEVEL = 0
 const val MAX_LEVEL = 10000
+private const val UPDATE_DELAY_IN_MILLIS = 2000L
 
 class ControlViewHolder(
     val layout: ViewGroup,
-    val controlsController: ControlsController
+    val controlsController: ControlsController,
+    val uiExecutor: DelayableExecutor
 ) {
     val icon: ImageView = layout.requireViewById(R.id.icon)
     val status: TextView = layout.requireViewById(R.id.status)
@@ -52,6 +55,7 @@
     val clipLayer: ClipDrawable
     val gd: GradientDrawable
     lateinit var cws: ControlWithState
+    var cancelUpdate: Runnable? = null
 
     init {
         val ld = layout.getBackground() as LayerDrawable
@@ -63,6 +67,8 @@
     fun bindData(cws: ControlWithState) {
         this.cws = cws
 
+        cancelUpdate?.run()
+
         val (status, template) = cws.control?.let {
             title.setText(it.getTitle())
             subtitle.setText(it.getSubtitle())
@@ -86,6 +92,27 @@
         findBehavior(status, template).apply(this, cws)
     }
 
+    fun actionResponse(@ControlAction.ResponseResult response: Int) {
+        val text = when (response) {
+            ControlAction.RESPONSE_OK -> "Success"
+            ControlAction.RESPONSE_FAIL -> "Error"
+            else -> ""
+        }
+
+        if (!text.isEmpty()) {
+            val previousText = status.getText()
+            val previousTextExtra = statusExtra.getText()
+
+            cancelUpdate = uiExecutor.executeDelayed({
+                    status.setText(previousText)
+                    statusExtra.setText(previousTextExtra)
+                }, UPDATE_DELAY_IN_MILLIS)
+
+            status.setText(text)
+            statusExtra.setText("")
+        }
+    }
+
     fun action(action: ControlAction) {
         controlsController.action(cws.ci, action)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
index b07a75d..d70c86f 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
@@ -22,6 +22,8 @@
 import android.view.ViewGroup
 
 interface ControlsUiController {
+    val available: Boolean
+
     fun show(parent: ViewGroup)
     fun hide()
     fun onRefreshState(componentName: ComponentName, controls: List<Control>)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index a777faf..ed521e3 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -38,10 +38,10 @@
 import com.android.systemui.controls.management.ControlsProviderSelectorActivity
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.R
+import com.android.systemui.util.concurrency.DelayableExecutor
 
 import dagger.Lazy
 
-import java.util.concurrent.Executor
 import javax.inject.Inject
 import javax.inject.Singleton
 
@@ -104,18 +104,23 @@
     }
 }
 
+private data class ControlKey(val componentName: ComponentName, val controlId: String)
+
 @Singleton
 class ControlsUiControllerImpl @Inject constructor (
     val controlsController: Lazy<ControlsController>,
     val context: Context,
-    @Main val uiExecutor: Executor
+    @Main val uiExecutor: DelayableExecutor
 ) : ControlsUiController {
 
     private lateinit var controlInfos: List<ControlInfo>
-    private val controlsById = mutableMapOf<Pair<ComponentName, String>, ControlWithState>()
-    private val controlViewsById = mutableMapOf<String, ControlViewHolder>()
+    private val controlsById = mutableMapOf<ControlKey, ControlWithState>()
+    private val controlViewsById = mutableMapOf<ControlKey, ControlViewHolder>()
     private lateinit var parent: ViewGroup
 
+    override val available: Boolean
+        get() = controlsController.get().available
+
     override fun show(parent: ViewGroup) {
         Log.d(TAG, "show()")
 
@@ -125,7 +130,7 @@
 
         controlInfos.map {
             ControlWithState(it, null)
-        }.associateByTo(controlsById) { Pair(it.ci.component, it.ci.controlId) }
+        }.associateByTo(controlsById) { ControlKey(it.ci.component, it.ci.controlId) }
 
         if (controlInfos.isEmpty()) {
             showInitialSetupView()
@@ -178,9 +183,10 @@
             val item = inflater.inflate(
                 R.layout.controls_base_item, lastRow, false) as ViewGroup
             lastRow.addView(item)
-            val cvh = ControlViewHolder(item, controlsController.get())
-            cvh.bindData(controlsById.get(Pair(it.component, it.controlId))!!)
-            controlViewsById.put(it.controlId, cvh)
+            val cvh = ControlViewHolder(item, controlsController.get(), uiExecutor)
+            val key = ControlKey(it.component, it.controlId)
+            cvh.bindData(controlsById.getValue(key))
+            controlViewsById.put(key, cvh)
         }
 
         if ((controlInfos.size % 2) == 1) {
@@ -205,21 +211,24 @@
     override fun onRefreshState(componentName: ComponentName, controls: List<Control>) {
         Log.d(TAG, "onRefreshState()")
         controls.forEach { c ->
-            controlsById.get(Pair(componentName, c.getControlId()))?.let {
+            controlsById.get(ControlKey(componentName, c.getControlId()))?.let {
                 Log.d(TAG, "onRefreshState() for id: " + c.getControlId())
                 val cws = ControlWithState(it.ci, c)
-                controlsById.put(Pair(componentName, c.getControlId()), cws)
+                val key = ControlKey(componentName, c.getControlId())
+                controlsById.put(key, cws)
 
                 uiExecutor.execute {
-                    controlViewsById.get(c.getControlId())?.bindData(cws)
+                    controlViewsById.get(key)?.bindData(cws)
                 }
             }
         }
     }
 
     override fun onActionResponse(componentName: ComponentName, controlId: String, response: Int) {
-        Log.d(TAG, "onActionResponse()")
-        TODO("not implemented")
+        val key = ControlKey(componentName, controlId)
+        uiExecutor.execute {
+            controlViewsById.get(key)?.actionResponse(response)
+        }
     }
 
     private fun createRow(inflater: LayoutInflater, parent: ViewGroup): ViewGroup {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
index 093c99f..da52c6f 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
@@ -108,10 +108,18 @@
     DeviceTypes.TYPE_OUTLET to IconState(
         R.drawable.ic_power_off_gm2_24px,
         R.drawable.ic_power_gm2_24px
+    ),
+    DeviceTypes.TYPE_VACUUM to IconState(
+        R.drawable.ic_vacuum_gm2_24px,
+        R.drawable.ic_vacuum_gm2_24px
+    ),
+    DeviceTypes.TYPE_MOP to IconState(
+        R.drawable.ic_vacuum_gm2_24px,
+        R.drawable.ic_vacuum_gm2_24px
     )
 ).withDefault {
     IconState(
-        R.drawable.ic_light_off_gm2_24px,
-        R.drawable.ic_lightbulb_outline_gm2_24px
+        R.drawable.ic_device_unknown_gm2_24px,
+        R.drawable.ic_device_unknown_gm2_24px
     )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 45c07a3..082b065 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -1899,9 +1899,7 @@
     }
 
     private boolean shouldShowControls() {
-        return isCurrentUserOwner()
-                && !mKeyguardManager.isDeviceLocked()
-                && Settings.Secure.getInt(mContext.getContentResolver(),
-                        "systemui.controls_available", 0) == 1;
+        return !mKeyguardManager.isDeviceLocked()
+                && mControlsUiController.getAvailable();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index 0134aa3..5de6d1c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -169,7 +169,7 @@
         if (DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)) {
             v.setText(mContext.getString(
                     com.android.internal.R.string.bugreport_status,
-                    Build.VERSION.RELEASE,
+                    Build.VERSION.RELEASE_OR_CODENAME,
                     Build.ID));
             v.setVisibility(View.VISIBLE);
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
index 9e3e94c..6c69718 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
@@ -36,6 +36,7 @@
 import android.media.session.MediaController;
 import android.media.session.MediaSession;
 import android.media.session.PlaybackState;
+import android.os.Handler;
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -370,6 +371,13 @@
         if (mSeamless == null) {
             return;
         }
+        Handler handler = mSeamless.getHandler();
+        handler.post(() -> {
+            updateChipInternal(device);
+        });
+    }
+
+    private void updateChipInternal(MediaDevice device) {
         ColorStateList fgTintList = ColorStateList.valueOf(mForegroundColor);
 
         // Update the outline color
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index b091ad8..626f298 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.screenrecord;
 
-import android.app.Activity;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
@@ -26,16 +25,21 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Icon;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.VirtualDisplay;
 import android.media.MediaRecorder;
+import android.media.projection.IMediaProjection;
+import android.media.projection.IMediaProjectionManager;
 import android.media.projection.MediaProjection;
 import android.media.projection.MediaProjectionManager;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.provider.MediaStore;
 import android.provider.Settings;
 import android.util.DisplayMetrics;
@@ -83,7 +87,6 @@
     private static final int AUDIO_SAMPLE_RATE = 44100;
 
     private final RecordingController mController;
-    private MediaProjectionManager mMediaProjectionManager;
     private MediaProjection mMediaProjection;
     private Surface mInputSurface;
     private VirtualDisplay mVirtualDisplay;
@@ -134,13 +137,30 @@
 
         switch (action) {
             case ACTION_START:
-                int resultCode = intent.getIntExtra(EXTRA_RESULT_CODE, Activity.RESULT_CANCELED);
                 mUseAudio = intent.getBooleanExtra(EXTRA_USE_AUDIO, false);
                 mShowTaps = intent.getBooleanExtra(EXTRA_SHOW_TAPS, false);
-                Intent data = intent.getParcelableExtra(EXTRA_DATA);
-                if (data != null) {
-                    mMediaProjection = mMediaProjectionManager.getMediaProjection(resultCode, data);
+                try {
+                    IBinder b = ServiceManager.getService(MEDIA_PROJECTION_SERVICE);
+                    IMediaProjectionManager mediaService =
+                            IMediaProjectionManager.Stub.asInterface(b);
+                    IMediaProjection proj = mediaService.createProjection(getUserId(),
+                            getPackageName(),
+                            MediaProjectionManager.TYPE_SCREEN_CAPTURE, false);
+                    IBinder projection = proj.asBinder();
+                    if (projection == null) {
+                        Log.e(TAG, "Projection was null");
+                        Toast.makeText(this, R.string.screenrecord_start_error, Toast.LENGTH_LONG)
+                                .show();
+                        return Service.START_NOT_STICKY;
+                    }
+                    mMediaProjection = new MediaProjection(getApplicationContext(),
+                            IMediaProjection.Stub.asInterface(projection));
                     startRecording();
+                } catch (RemoteException e) {
+                    e.printStackTrace();
+                    Toast.makeText(this, R.string.screenrecord_start_error, Toast.LENGTH_LONG)
+                            .show();
+                    return Service.START_NOT_STICKY;
                 }
                 break;
 
@@ -195,9 +215,6 @@
     @Override
     public void onCreate() {
         super.onCreate();
-
-        mMediaProjectionManager =
-                (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
     }
 
     /**
@@ -269,6 +286,7 @@
     }
 
     private void createRecordingNotification() {
+        Resources res = getResources();
         NotificationChannel channel = new NotificationChannel(
                 CHANNEL_ID,
                 getString(R.string.screenrecord_name),
@@ -281,11 +299,15 @@
 
         Bundle extras = new Bundle();
         extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
-                getResources().getString(R.string.screenrecord_name));
+                res.getString(R.string.screenrecord_name));
+
+        String notificationTitle = mUseAudio
+                ? res.getString(R.string.screenrecord_ongoing_screen_and_audio)
+                : res.getString(R.string.screenrecord_ongoing_screen_only);
 
         mRecordingNotificationBuilder = new Notification.Builder(this, CHANNEL_ID)
                 .setSmallIcon(R.drawable.ic_screenrecord)
-                .setContentTitle(getResources().getString(R.string.screenrecord_name))
+                .setContentTitle(notificationTitle)
                 .setContentText(getResources().getString(R.string.screenrecord_stop_text))
                 .setUsesChronometer(true)
                 .setColorized(true)
@@ -332,8 +354,7 @@
 
         Notification.Builder builder = new Notification.Builder(this, CHANNEL_ID)
                 .setSmallIcon(R.drawable.ic_screenrecord)
-                .setContentTitle(getResources().getString(R.string.screenrecord_name))
-                .setContentText(getResources().getString(R.string.screenrecord_save_message))
+                .setContentTitle(getResources().getString(R.string.screenrecord_save_message))
                 .setContentIntent(PendingIntent.getActivity(
                         this,
                         REQUEST_CODE,
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
index 8324986..566f12b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
@@ -16,15 +16,14 @@
 
 package com.android.systemui.screenrecord;
 
-import android.Manifest;
 import android.app.Activity;
 import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.media.projection.MediaProjectionManager;
 import android.os.Bundle;
-import android.widget.Toast;
+import android.view.Gravity;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.Switch;
 
 import com.android.systemui.R;
 
@@ -34,15 +33,11 @@
  * Activity to select screen recording options
  */
 public class ScreenRecordDialog extends Activity {
-    private static final int REQUEST_CODE_VIDEO_ONLY = 200;
-    private static final int REQUEST_CODE_VIDEO_TAPS = 201;
-    private static final int REQUEST_CODE_PERMISSIONS = 299;
-    private static final int REQUEST_CODE_VIDEO_AUDIO = 300;
-    private static final int REQUEST_CODE_VIDEO_AUDIO_TAPS = 301;
-    private static final int REQUEST_CODE_PERMISSIONS_AUDIO = 399;
     private static final long DELAY_MS = 3000;
 
     private final RecordingController mController;
+    private Switch mAudioSwitch;
+    private Switch mTapsSwitch;
 
     @Inject
     public ScreenRecordDialog(RecordingController controller) {
@@ -52,81 +47,42 @@
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        requestScreenCapture();
+
+        Window window = getWindow();
+        // Inflate the decor view, so the attributes below are not overwritten by the theme.
+        window.getDecorView();
+        window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+        window.setGravity(Gravity.TOP);
+
+        setContentView(R.layout.screen_record_dialog);
+
+        Button cancelBtn = findViewById(R.id.button_cancel);
+        cancelBtn.setOnClickListener(v -> {
+            finish();
+        });
+
+        Button startBtn = findViewById(R.id.button_start);
+        startBtn.setOnClickListener(v -> {
+            requestScreenCapture();
+            finish();
+        });
+
+        mAudioSwitch = findViewById(R.id.screenrecord_audio_switch);
+        mTapsSwitch = findViewById(R.id.screenrecord_taps_switch);
     }
 
     private void requestScreenCapture() {
-        MediaProjectionManager mediaProjectionManager = (MediaProjectionManager) getSystemService(
-                Context.MEDIA_PROJECTION_SERVICE);
-        Intent permissionIntent = mediaProjectionManager.createScreenCaptureIntent();
-
-        // TODO get saved settings
-        boolean useAudio = false;
-        boolean showTaps = false;
-        if (useAudio) {
-            startActivityForResult(permissionIntent,
-                    showTaps ? REQUEST_CODE_VIDEO_AUDIO_TAPS : REQUEST_CODE_VIDEO_AUDIO);
-        } else {
-            startActivityForResult(permissionIntent,
-                    showTaps ? REQUEST_CODE_VIDEO_TAPS : REQUEST_CODE_VIDEO_ONLY);
-        }
-    }
-
-    @Override
-    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
-        boolean showTaps = (requestCode == REQUEST_CODE_VIDEO_TAPS
-                || requestCode == REQUEST_CODE_VIDEO_AUDIO_TAPS);
-        boolean useAudio = (requestCode == REQUEST_CODE_VIDEO_AUDIO
-                || requestCode == REQUEST_CODE_VIDEO_AUDIO_TAPS);
-        switch (requestCode) {
-            case REQUEST_CODE_VIDEO_TAPS:
-            case REQUEST_CODE_VIDEO_AUDIO_TAPS:
-            case REQUEST_CODE_VIDEO_ONLY:
-            case REQUEST_CODE_VIDEO_AUDIO:
-                if (resultCode == RESULT_OK) {
-                    PendingIntent startIntent = PendingIntent.getForegroundService(
-                            this, RecordingService.REQUEST_CODE, RecordingService.getStartIntent(
-                                    ScreenRecordDialog.this, resultCode, data, useAudio,
-                                    showTaps),
-                            PendingIntent.FLAG_UPDATE_CURRENT
-                            );
-                    PendingIntent stopIntent = PendingIntent.getService(
-                            this, RecordingService.REQUEST_CODE,
-                            RecordingService.getStopIntent(this),
-                            PendingIntent.FLAG_UPDATE_CURRENT);
-                    mController.startCountdown(DELAY_MS, startIntent, stopIntent);
-                } else {
-                    Toast.makeText(this,
-                            getResources().getString(R.string.screenrecord_permission_error),
-                            Toast.LENGTH_SHORT).show();
-                }
-                finish();
-                break;
-            case REQUEST_CODE_PERMISSIONS:
-                int permission = checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
-                if (permission != PackageManager.PERMISSION_GRANTED) {
-                    Toast.makeText(this,
-                            getResources().getString(R.string.screenrecord_permission_error),
-                            Toast.LENGTH_SHORT).show();
-                    finish();
-                } else {
-                    requestScreenCapture();
-                }
-                break;
-            case REQUEST_CODE_PERMISSIONS_AUDIO:
-                int videoPermission = checkSelfPermission(
-                        Manifest.permission.WRITE_EXTERNAL_STORAGE);
-                int audioPermission = checkSelfPermission(Manifest.permission.RECORD_AUDIO);
-                if (videoPermission != PackageManager.PERMISSION_GRANTED
-                        || audioPermission != PackageManager.PERMISSION_GRANTED) {
-                    Toast.makeText(this,
-                            getResources().getString(R.string.screenrecord_permission_error),
-                            Toast.LENGTH_SHORT).show();
-                    finish();
-                } else {
-                    requestScreenCapture();
-                }
-                break;
-        }
+        boolean useAudio = mAudioSwitch.isChecked();
+        boolean showTaps = mTapsSwitch.isChecked();
+        PendingIntent startIntent = PendingIntent.getForegroundService(this,
+                RecordingService.REQUEST_CODE,
+                RecordingService.getStartIntent(
+                        ScreenRecordDialog.this, RESULT_OK, null, useAudio, showTaps),
+                PendingIntent.FLAG_UPDATE_CURRENT);
+        PendingIntent stopIntent = PendingIntent.getService(this,
+                RecordingService.REQUEST_CODE,
+                RecordingService.getStopIntent(this),
+                PendingIntent.FLAG_UPDATE_CURRENT);
+        mController.startCountdown(DELAY_MS, startIntent, stopIntent);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 2907cd4..8dfcb0a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -134,6 +134,8 @@
         mBroadcastDispatcher.registerReceiver(
                 mReceiver, filter, null /* handler */, UserHandle.SYSTEM);
 
+        mSimpleUserSwitcher = shouldUseSimpleUserSwitcher();
+
         mSecondaryUserServiceIntent = new Intent(context, SystemUISecondaryUserService.class);
 
         filter = new IntentFilter();
@@ -258,22 +260,20 @@
                         && mUserManager.canAddMoreUsers();
                 boolean createIsRestricted = !addUsersWhenLocked;
 
-                if (!mSimpleUserSwitcher) {
-                    if (guestRecord == null) {
-                        if (canCreateGuest) {
-                            guestRecord = new UserRecord(null /* info */, null /* picture */,
-                                    true /* isGuest */, false /* isCurrent */,
-                                    false /* isAddUser */, createIsRestricted, canSwitchUsers);
-                            checkIfAddUserDisallowedByAdminOnly(guestRecord);
-                            records.add(guestRecord);
-                        }
-                    } else {
-                        int index = guestRecord.isCurrent ? 0 : records.size();
-                        records.add(index, guestRecord);
+                if (guestRecord == null) {
+                    if (canCreateGuest) {
+                        guestRecord = new UserRecord(null /* info */, null /* picture */,
+                                true /* isGuest */, false /* isCurrent */,
+                                false /* isAddUser */, createIsRestricted, canSwitchUsers);
+                        checkIfAddUserDisallowedByAdminOnly(guestRecord);
+                        records.add(guestRecord);
                     }
+                } else {
+                    int index = guestRecord.isCurrent ? 0 : records.size();
+                    records.add(index, guestRecord);
                 }
 
-                if (!mSimpleUserSwitcher && canCreateUser) {
+                if (canCreateUser) {
                     UserRecord addUserRecord = new UserRecord(null /* info */, null /* picture */,
                             false /* isGuest */, false /* isCurrent */, true /* isAddUser */,
                             createIsRestricted, canSwitchUsers);
@@ -562,8 +562,7 @@
 
     private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) {
         public void onChange(boolean selfChange) {
-            mSimpleUserSwitcher = Settings.Global.getInt(mContext.getContentResolver(),
-                    SIMPLE_USER_SWITCHER_GLOBAL_SETTING, 0) != 0;
+            mSimpleUserSwitcher = shouldUseSimpleUserSwitcher();
             mAddUsersWhenLocked = Settings.Global.getInt(mContext.getContentResolver(),
                     Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0;
             refreshUsers(UserHandle.USER_NULL);
@@ -579,6 +578,7 @@
             final UserRecord u = mUsers.get(i);
             pw.print("    "); pw.println(u.toString());
         }
+        pw.println("mSimpleUserSwitcher=" + mSimpleUserSwitcher);
     }
 
     public String getCurrentUserName(Context context) {
@@ -717,6 +717,13 @@
         }
     }
 
+    private boolean shouldUseSimpleUserSwitcher() {
+        int defaultSimpleUserSwitcher = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_expandLockScreenUserSwitcher) ? 1 : 0;
+        return Settings.Global.getInt(mContext.getContentResolver(),
+                SIMPLE_USER_SWITCHER_GLOBAL_SETTING, defaultSimpleUserSwitcher) != 0;
+    }
+
     public void startActivity(Intent intent) {
         mActivityStarter.startActivity(intent, true);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java
index a60ca62..49ada1a 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java
@@ -158,7 +158,7 @@
 
         String demoTime = "1010"; // 10:10, a classic choice of horologists
         try {
-            String[] versionParts = android.os.Build.VERSION.RELEASE.split("\\.");
+            String[] versionParts = android.os.Build.VERSION.RELEASE_OR_CODENAME.split("\\.");
             int majorVersion = Integer.valueOf(versionParts[0]);
             demoTime = String.format("%02d00", majorVersion % 24);
         } catch (IllegalArgumentException ex) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
index 897091f..e3bcdc8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
@@ -32,12 +32,13 @@
 import com.android.systemui.DumpController
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.controls.management.ControlsListingController
 import com.android.systemui.controls.ControlStatus
+import com.android.systemui.controls.management.ControlsListingController
 import com.android.systemui.controls.ui.ControlsUiController
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.time.FakeSystemClock
 import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
 import org.junit.Before
 import org.junit.Test
@@ -82,7 +83,7 @@
     private lateinit var broadcastReceiverCaptor: ArgumentCaptor<BroadcastReceiver>
 
     private lateinit var delayableExecutor: FakeExecutor
-    private lateinit var controller: ControlsController
+    private lateinit var controller: ControlsControllerImpl
 
     companion object {
         fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
@@ -416,5 +417,70 @@
         verify(listingController).changeUser(UserHandle.of(otherUser))
         assertTrue(controller.getFavoriteControls().isEmpty())
         assertEquals(otherUser, controller.currentUserId)
+        assertTrue(controller.available)
     }
-}
\ No newline at end of file
+
+    @Test
+    fun testDisableFeature_notAvailable() {
+        Settings.Secure.putIntForUser(mContext.contentResolver,
+                ControlsControllerImpl.CONTROLS_AVAILABLE, 0, user)
+        controller.settingObserver.onChange(false, ControlsControllerImpl.URI, 0)
+        assertFalse(controller.available)
+    }
+
+    @Test
+    fun testDisableFeature_clearFavorites() {
+        controller.changeFavoriteStatus(TEST_CONTROL_INFO, true)
+        assertFalse(controller.getFavoriteControls().isEmpty())
+
+        Settings.Secure.putIntForUser(mContext.contentResolver,
+                ControlsControllerImpl.CONTROLS_AVAILABLE, 0, user)
+        controller.settingObserver.onChange(false, ControlsControllerImpl.URI, user)
+        assertTrue(controller.getFavoriteControls().isEmpty())
+    }
+
+    @Test
+    fun testDisableFeature_noChangeForNotCurrentUser() {
+        controller.changeFavoriteStatus(TEST_CONTROL_INFO, true)
+        Settings.Secure.putIntForUser(mContext.contentResolver,
+                ControlsControllerImpl.CONTROLS_AVAILABLE, 0, otherUser)
+        controller.settingObserver.onChange(false, ControlsControllerImpl.URI, otherUser)
+
+        assertTrue(controller.available)
+        assertFalse(controller.getFavoriteControls().isEmpty())
+    }
+
+    @Test
+    fun testCorrectUserSettingOnUserChange() {
+        Settings.Secure.putIntForUser(mContext.contentResolver,
+                ControlsControllerImpl.CONTROLS_AVAILABLE, 0, otherUser)
+
+        val intent = Intent(Intent.ACTION_USER_SWITCHED).apply {
+            putExtra(Intent.EXTRA_USER_HANDLE, otherUser)
+        }
+        val pendingResult = mock(BroadcastReceiver.PendingResult::class.java)
+        `when`(pendingResult.sendingUserId).thenReturn(otherUser)
+        broadcastReceiverCaptor.value.pendingResult = pendingResult
+
+        broadcastReceiverCaptor.value.onReceive(mContext, intent)
+
+        assertFalse(controller.available)
+    }
+
+    @Test
+    fun testCountFavoritesForComponent_singleComponent() {
+        controller.changeFavoriteStatus(TEST_CONTROL_INFO, true)
+
+        assertEquals(1, controller.countFavoritesForComponent(TEST_COMPONENT))
+        assertEquals(0, controller.countFavoritesForComponent(TEST_COMPONENT_2))
+    }
+
+    @Test
+    fun testCountFavoritesForComponent_multipleComponents() {
+        controller.changeFavoriteStatus(TEST_CONTROL_INFO, true)
+        controller.changeFavoriteStatus(TEST_CONTROL_INFO_2, true)
+
+        assertEquals(1, controller.countFavoritesForComponent(TEST_COMPONENT))
+        assertEquals(1, controller.countFavoritesForComponent(TEST_COMPONENT_2))
+    }
+}
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 2fc9d04..c4e6427 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -16,6 +16,11 @@
 
 package com.android.server.compat;
 
+import static android.Manifest.permission.LOG_COMPAT_CHANGE;
+import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG;
+import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
 import android.app.ActivityManager;
 import android.app.IActivityManager;
 import android.content.Context;
@@ -67,12 +72,14 @@
 
     @Override
     public void reportChange(long changeId, ApplicationInfo appInfo) {
+        checkCompatChangeLogPermission();
         reportChange(changeId, appInfo.uid,
                 ChangeReporter.STATE_LOGGED);
     }
 
     @Override
     public void reportChangeByPackageName(long changeId, String packageName, int userId) {
+        checkCompatChangeLogPermission();
         ApplicationInfo appInfo = getApplicationInfo(packageName, userId);
         if (appInfo == null) {
             return;
@@ -82,11 +89,13 @@
 
     @Override
     public void reportChangeByUid(long changeId, int uid) {
+        checkCompatChangeLogPermission();
         reportChange(changeId, uid, ChangeReporter.STATE_LOGGED);
     }
 
     @Override
     public boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) {
+        checkCompatChangeReadAndLogPermission();
         if (mCompatConfig.isChangeEnabled(changeId, appInfo)) {
             reportChange(changeId, appInfo.uid,
                     ChangeReporter.STATE_ENABLED);
@@ -99,6 +108,7 @@
 
     @Override
     public boolean isChangeEnabledByPackageName(long changeId, String packageName, int userId) {
+        checkCompatChangeReadAndLogPermission();
         ApplicationInfo appInfo = getApplicationInfo(packageName, userId);
         if (appInfo == null) {
             return true;
@@ -108,6 +118,7 @@
 
     @Override
     public boolean isChangeEnabledByUid(long changeId, int uid) {
+        checkCompatChangeReadAndLogPermission();
         String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
         if (packages == null || packages.length == 0) {
             return true;
@@ -140,6 +151,7 @@
     @Override
     public void setOverrides(CompatibilityChangeConfig overrides, String packageName)
             throws RemoteException, SecurityException {
+        checkCompatChangeOverridePermission();
         mCompatConfig.addOverrides(overrides, packageName);
         killPackage(packageName);
     }
@@ -147,11 +159,13 @@
     @Override
     public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName)
             throws RemoteException, SecurityException {
+        checkCompatChangeOverridePermission();
         mCompatConfig.addOverrides(overrides, packageName);
     }
 
     @Override
     public void clearOverrides(String packageName) throws RemoteException, SecurityException {
+        checkCompatChangeOverridePermission();
         mCompatConfig.removePackageOverrides(packageName);
         killPackage(packageName);
     }
@@ -159,12 +173,14 @@
     @Override
     public void clearOverridesForTest(String packageName)
             throws RemoteException, SecurityException {
+        checkCompatChangeOverridePermission();
         mCompatConfig.removePackageOverrides(packageName);
     }
 
     @Override
     public boolean clearOverride(long changeId, String packageName)
             throws RemoteException, SecurityException {
+        checkCompatChangeOverridePermission();
         boolean existed = mCompatConfig.removeOverride(changeId, packageName);
         killPackage(packageName);
         return existed;
@@ -172,11 +188,13 @@
 
     @Override
     public CompatibilityChangeConfig getAppConfig(ApplicationInfo appInfo) {
+        checkCompatChangeReadAndLogPermission();
         return mCompatConfig.getAppConfig(appInfo);
     }
 
     @Override
     public CompatibilityChangeInfo[] listAllChanges() {
+        checkCompatChangeReadPermission();
         return mCompatConfig.dumpChanges();
     }
 
@@ -215,6 +233,7 @@
 
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        checkCompatChangeReadAndLogPermission();
         if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) return;
         mCompatConfig.dumpConfig(pw);
     }
@@ -272,4 +291,30 @@
             Binder.restoreCallingIdentity(identity);
         }
     }
+
+    private void checkCompatChangeLogPermission() throws SecurityException {
+        if (mContext.checkCallingOrSelfPermission(LOG_COMPAT_CHANGE)
+                != PERMISSION_GRANTED) {
+            throw new SecurityException("Cannot log compat change usage");
+        }
+    }
+
+    private void checkCompatChangeReadPermission() throws SecurityException {
+        if (mContext.checkCallingOrSelfPermission(READ_COMPAT_CHANGE_CONFIG)
+                != PERMISSION_GRANTED) {
+            throw new SecurityException("Cannot read compat change");
+        }
+    }
+
+    private void checkCompatChangeOverridePermission() throws SecurityException {
+        if (mContext.checkCallingOrSelfPermission(OVERRIDE_COMPAT_CHANGE_CONFIG)
+                != PERMISSION_GRANTED) {
+            throw new SecurityException("Cannot override compat change");
+        }
+    }
+
+    private void checkCompatChangeReadAndLogPermission() throws SecurityException {
+        checkCompatChangeReadPermission();
+        checkCompatChangeLogPermission();
+    }
 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index b52289e..f071135 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2681,18 +2681,24 @@
         @Override
         public void enqueueTextToast(String pkg, IBinder token, CharSequence text, int duration,
                 int displayId, @Nullable ITransientNotificationCallback callback) {
-            enqueueToast(pkg, token, text, null, duration, displayId, callback);
+            enqueueToast(pkg, token, text, null, duration, displayId, callback, false);
         }
 
         @Override
         public void enqueueToast(String pkg, IBinder token, ITransientNotification callback,
                 int duration, int displayId) {
-            enqueueToast(pkg, token, null, callback, duration, displayId, null);
+            enqueueToast(pkg, token, null, callback, duration, displayId, null, true);
+        }
+
+        @Override
+        public void enqueueTextOrCustomToast(String pkg, IBinder token,
+                ITransientNotification callback, int duration, int displayId, boolean isCustom) {
+            enqueueToast(pkg, token, null, callback, duration, displayId, null, isCustom);
         }
 
         private void enqueueToast(String pkg, IBinder token, @Nullable CharSequence text,
                 @Nullable ITransientNotification callback, int duration, int displayId,
-                @Nullable ITransientNotificationCallback textCallback) {
+                @Nullable ITransientNotificationCallback textCallback, boolean isCustom) {
             if (DBG) {
                 Slog.i(TAG, "enqueueToast pkg=" + pkg + " token=" + token
                         + " duration=" + duration + " displayId=" + displayId);
@@ -2730,7 +2736,7 @@
                 return;
             }
 
-            if (callback != null && !appIsForeground && !isSystemToast) {
+            if (callback != null && !appIsForeground && !isSystemToast && isCustom) {
                 boolean block;
                 long id = Binder.clearCallingIdentity();
                 try {
diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
index 639cc70..90fc59a 100644
--- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
+++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
@@ -38,6 +38,8 @@
 import android.util.LruCache;
 import android.util.Slog;
 
+import libcore.util.EmptyArray;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.LinkedList;
@@ -301,7 +303,7 @@
         for (String person: second) {
             people.add(person);
         }
-        return (String[]) people.toArray();
+        return people.toArray(EmptyArray.STRING);
     }
 
     @Nullable
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index bf7bebd..0cf8b42 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -129,6 +129,7 @@
 import com.android.server.security.VerityUtils;
 
 import libcore.io.IoUtils;
+import libcore.util.EmptyArray;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -212,7 +213,8 @@
     private static final String ATTR_SIGNATURE = "signature";
 
     private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill";
-    private static final int[] EMPTY_CHILD_SESSION_ARRAY = {};
+    private static final int[] EMPTY_CHILD_SESSION_ARRAY = EmptyArray.INT;
+    private static final FileInfo[] EMPTY_FILE_INFO_ARRAY = {};
 
     private static final String SYSTEM_DATA_LOADER_PACKAGE = "android";
 
@@ -375,8 +377,6 @@
     // TODO(b/146080380): merge file list with Callback installation.
     private IncrementalFileStorages mIncrementalFileStorages;
 
-    private static final String[] EMPTY_STRING_ARRAY = new String[]{};
-
     private static final FileFilter sAddedApkFilter = new FileFilter() {
         @Override
         public boolean accept(File file) {
@@ -558,10 +558,22 @@
         mStagedSessionErrorMessage =
                 stagedSessionErrorMessage != null ? stagedSessionErrorMessage : "";
 
-        if (isStreamingInstallation()
-                && this.params.dataLoaderParams.getComponentName().getPackageName()
-                == SYSTEM_DATA_LOADER_PACKAGE) {
-            assertShellOrSystemCalling("System data loaders");
+        if (isDataLoaderInstallation()) {
+            if (isApexInstallation()) {
+                throw new IllegalArgumentException(
+                        "DataLoader installation of APEX modules is not allowed.");
+            }
+        }
+
+        if (isStreamingInstallation()) {
+            if (!isIncrementalInstallationAllowed(mPackageName)) {
+                throw new IllegalArgumentException(
+                        "Incremental installation of this package is not allowed.");
+            }
+            if (this.params.dataLoaderParams.getComponentName().getPackageName()
+                    == SYSTEM_DATA_LOADER_PACKAGE) {
+                assertShellOrSystemCalling("System data loaders");
+            }
         }
     }
 
@@ -719,7 +731,7 @@
         if (!isDataLoaderInstallation()) {
             String[] result = stageDir.list();
             if (result == null) {
-                result = EMPTY_STRING_ARRAY;
+                result = EmptyArray.STRING;
             }
             return result;
         }
@@ -1174,6 +1186,19 @@
     }
 
     /**
+     * Checks if the package can be installed on IncFs.
+     */
+    private static boolean isIncrementalInstallationAllowed(String packageName) {
+        final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
+        final AndroidPackage existingPackage = pmi.getPackage(packageName);
+        if (existingPackage == null) {
+            return true;
+        }
+
+        return !PackageManagerService.isSystemApp(existingPackage);
+    }
+
+    /**
      * If this was not already called, the session will be sealed.
      *
      * This method may be called multiple times to update the status receiver validate caller
@@ -1362,14 +1387,10 @@
                     return false;
                 }
 
-                final PackageInfo pkgInfo = mPm.getPackageInfo(
-                        params.appPackageName, PackageManager.GET_SIGNATURES
-                                | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
-
                 if (isApexInstallation()) {
                     validateApexInstallLocked();
                 } else {
-                    validateApkInstallLocked(pkgInfo);
+                    validateApkInstallLocked();
                 }
             }
 
@@ -1786,8 +1807,7 @@
      * {@link PackageManagerService}.
      */
     @GuardedBy("mLock")
-    private void validateApkInstallLocked(@Nullable PackageInfo pkgInfo)
-            throws PackageManagerException {
+    private void validateApkInstallLocked() throws PackageManagerException {
         ApkLite baseApk = null;
         mPackageName = null;
         mVersionCode = -1;
@@ -1797,6 +1817,10 @@
         mResolvedStagedFiles.clear();
         mResolvedInheritedFiles.clear();
 
+        final PackageInfo pkgInfo = mPm.getPackageInfo(
+                params.appPackageName, PackageManager.GET_SIGNATURES
+                        | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
+
         // Partial installs must be consistent with existing install
         if (params.mode == SessionParams.MODE_INHERIT_EXISTING
                 && (pkgInfo == null || pkgInfo.applicationInfo == null)) {
@@ -3096,7 +3120,8 @@
         }
 
         if (grantedRuntimePermissions.size() > 0) {
-            params.grantedRuntimePermissions = (String[]) grantedRuntimePermissions.toArray();
+            params.grantedRuntimePermissions =
+                    grantedRuntimePermissions.toArray(EmptyArray.STRING);
         }
 
         if (whitelistedRestrictedPermissions.size() > 0) {
@@ -3115,7 +3140,7 @@
 
         FileInfo[] fileInfosArray = null;
         if (!files.isEmpty()) {
-            fileInfosArray = (FileInfo[]) files.toArray();
+            fileInfosArray = files.toArray(EMPTY_FILE_INFO_ARRAY);
         }
 
         InstallSource installSource = InstallSource.create(installInitiatingPackageName,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e4ec374..c8585907 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -150,6 +150,7 @@
 import android.content.pm.AuxiliaryResolveInfo;
 import android.content.pm.ChangedPackages;
 import android.content.pm.ComponentInfo;
+import android.content.pm.DataLoaderType;
 import android.content.pm.FallbackCategoryProvider;
 import android.content.pm.FeatureInfo;
 import android.content.pm.IDexModuleRegisterCallback;
@@ -1658,7 +1659,8 @@
                         handlePackagePostInstall(parentRes, grantPermissions,
                                 killApp, virtualPreload, grantedPermissions,
                                 whitelistedRestrictedPermissions, didRestore,
-                                args.installSource.installerPackageName, args.observer);
+                                args.installSource.installerPackageName, args.observer,
+                                args.mDataLoaderType);
 
                         // Handle the child packages
                         final int childCount = (parentRes.addedChildPackages != null)
@@ -1668,7 +1670,8 @@
                             handlePackagePostInstall(childRes, grantPermissions,
                                     killApp, virtualPreload, grantedPermissions,
                                     whitelistedRestrictedPermissions, false /*didRestore*/,
-                                    args.installSource.installerPackageName, args.observer);
+                                    args.installSource.installerPackageName, args.observer,
+                                    args.mDataLoaderType);
                         }
 
                         // Log tracing if needed
@@ -1995,7 +1998,7 @@
             boolean killApp, boolean virtualPreload,
             String[] grantedPermissions, List<String> whitelistedRestrictedPermissions,
             boolean launchedForRestore, String installerPackage,
-            IPackageInstallObserver2 installObserver) {
+            IPackageInstallObserver2 installObserver, int dataLoaderType) {
         final boolean succeeded = res.returnCode == PackageManager.INSTALL_SUCCEEDED;
         final boolean update = res.removedInfo != null && res.removedInfo.removedPackage != null;
 
@@ -2096,11 +2099,14 @@
                 if (update) {
                     extras.putBoolean(Intent.EXTRA_REPLACING, true);
                 }
+                extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
+                // Send to all running apps.
                 sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
                         extras, 0 /*flags*/,
                         null /*targetPackage*/, null /*finishedReceiver*/,
                         updateUserIds, instantUserIds);
                 if (installerPackageName != null) {
+                    // Send to the installer, even if it's not running.
                     sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
                             extras, 0 /*flags*/,
                             installerPackageName, null /*finishedReceiver*/,
@@ -14117,13 +14123,14 @@
         MultiPackageInstallParams mParentInstallParams;
         final long requiredInstalledVersionCode;
         final boolean forceQueryableOverride;
+        final int mDataLoaderType;
 
         InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
                 int installFlags, InstallSource installSource, String volumeUuid,
                 VerificationInfo verificationInfo, UserHandle user, String packageAbiOverride,
                 String[] grantedPermissions, List<String> whitelistedRestrictedPermissions,
                 SigningDetails signingDetails, int installReason,
-                long requiredInstalledVersionCode) {
+                long requiredInstalledVersionCode, int dataLoaderType) {
             super(user);
             this.origin = origin;
             this.move = move;
@@ -14139,40 +14146,42 @@
             this.installReason = installReason;
             this.requiredInstalledVersionCode = requiredInstalledVersionCode;
             this.forceQueryableOverride = false;
+            this.mDataLoaderType = dataLoaderType;
         }
 
         InstallParams(ActiveInstallSession activeInstallSession) {
             super(activeInstallSession.getUser());
+            final PackageInstaller.SessionParams sessionParams =
+                    activeInstallSession.getSessionParams();
             if (DEBUG_INSTANT) {
-                if ((activeInstallSession.getSessionParams().installFlags
+                if ((sessionParams.installFlags
                         & PackageManager.INSTALL_INSTANT_APP) != 0) {
                     Slog.d(TAG, "Ephemeral install of " + activeInstallSession.getPackageName());
                 }
             }
             verificationInfo = new VerificationInfo(
-                    activeInstallSession.getSessionParams().originatingUri,
-                    activeInstallSession.getSessionParams().referrerUri,
-                    activeInstallSession.getSessionParams().originatingUid,
+                    sessionParams.originatingUri,
+                    sessionParams.referrerUri,
+                    sessionParams.originatingUid,
                     activeInstallSession.getInstallerUid());
             origin = OriginInfo.fromStagedFile(activeInstallSession.getStagedDir());
             move = null;
             installReason = fixUpInstallReason(
                     activeInstallSession.getInstallSource().installerPackageName,
                     activeInstallSession.getInstallerUid(),
-                    activeInstallSession.getSessionParams().installReason);
+                    sessionParams.installReason);
             observer = activeInstallSession.getObserver();
-            installFlags = activeInstallSession.getSessionParams().installFlags;
+            installFlags = sessionParams.installFlags;
             installSource = activeInstallSession.getInstallSource();
-            volumeUuid = activeInstallSession.getSessionParams().volumeUuid;
-            packageAbiOverride = activeInstallSession.getSessionParams().abiOverride;
-            grantedRuntimePermissions = activeInstallSession.getSessionParams()
-                    .grantedRuntimePermissions;
-            whitelistedRestrictedPermissions = activeInstallSession.getSessionParams()
-                    .whitelistedRestrictedPermissions;
+            volumeUuid = sessionParams.volumeUuid;
+            packageAbiOverride = sessionParams.abiOverride;
+            grantedRuntimePermissions = sessionParams.grantedRuntimePermissions;
+            whitelistedRestrictedPermissions = sessionParams.whitelistedRestrictedPermissions;
             signingDetails = activeInstallSession.getSigningDetails();
-            requiredInstalledVersionCode = activeInstallSession.getSessionParams()
-                    .requiredInstalledVersionCode;
-            forceQueryableOverride = activeInstallSession.getSessionParams().forceQueryableOverride;
+            requiredInstalledVersionCode = sessionParams.requiredInstalledVersionCode;
+            forceQueryableOverride = sessionParams.forceQueryableOverride;
+            mDataLoaderType = (sessionParams.dataLoaderParams != null)
+                    ? sessionParams.dataLoaderParams.getType() : DataLoaderType.NONE;
         }
 
         @Override
@@ -14775,6 +14784,7 @@
         final int installReason;
         final boolean forceQueryableOverride;
         @Nullable final MultiPackageInstallParams mMultiPackageInstallParams;
+        final int mDataLoaderType;
 
         // The list of instruction sets supported by this app. This is currently
         // only used during the rmdex() phase to clean up resources. We can get rid of this
@@ -14788,7 +14798,7 @@
                 List<String> whitelistedRestrictedPermissions,
                 String traceMethod, int traceCookie, SigningDetails signingDetails,
                 int installReason, boolean forceQueryableOverride,
-                MultiPackageInstallParams multiPackageInstallParams) {
+                MultiPackageInstallParams multiPackageInstallParams, int dataLoaderType) {
             this.origin = origin;
             this.move = move;
             this.installFlags = installFlags;
@@ -14806,6 +14816,7 @@
             this.installReason = installReason;
             this.forceQueryableOverride = forceQueryableOverride;
             this.mMultiPackageInstallParams = multiPackageInstallParams;
+            this.mDataLoaderType = dataLoaderType;
         }
 
         /** New install */
@@ -14815,7 +14826,8 @@
                     params.getUser(), null /*instructionSets*/, params.packageAbiOverride,
                     params.grantedRuntimePermissions, params.whitelistedRestrictedPermissions,
                     params.traceMethod, params.traceCookie, params.signingDetails,
-                    params.installReason, params.forceQueryableOverride, params.mParentInstallParams);
+                    params.installReason, params.forceQueryableOverride,
+                    params.mParentInstallParams, params.mDataLoaderType);
         }
 
         abstract int copyApk();
@@ -14906,7 +14918,8 @@
             super(OriginInfo.fromNothing(), null, null, 0, InstallSource.EMPTY,
                     null, null, instructionSets, null, null, null, null, 0,
                     PackageParser.SigningDetails.UNKNOWN,
-                    PackageManager.INSTALL_REASON_UNKNOWN, false, null /* parent */);
+                    PackageManager.INSTALL_REASON_UNKNOWN, false, null /* parent */,
+                    DataLoaderType.NONE);
             this.codeFile = (codePath != null) ? new File(codePath) : null;
             this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null;
         }
@@ -14986,9 +14999,7 @@
             try {
                 makeDirRecursive(afterCodeFile.getParentFile(), 0775);
                 if (onIncremental) {
-                    // TODO(b/147371381): fix incremental installation
-                    mIncrementalManager.rename(beforeCodeFile.getAbsolutePath(),
-                            afterCodeFile.getAbsolutePath());
+                    mIncrementalManager.renameCodePath(beforeCodeFile, afterCodeFile);
                 } else {
                     Os.rename(beforeCodeFile.getAbsolutePath(), afterCodeFile.getAbsolutePath());
                 }
@@ -22257,7 +22268,8 @@
                 installSource, volumeUuid, null /*verificationInfo*/, user,
                 packageAbiOverride, null /*grantedPermissions*/,
                 null /*whitelistedRestrictedPermissions*/, PackageParser.SigningDetails.UNKNOWN,
-                PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.VERSION_CODE_HIGHEST);
+                PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.VERSION_CODE_HIGHEST,
+                DataLoaderType.NONE);
         params.setTraceMethod("movePackage").setTraceCookie(System.identityHashCode(params));
         msg.obj = params;
 
diff --git a/services/core/java/com/android/server/policy/LegacyGlobalActions.java b/services/core/java/com/android/server/policy/LegacyGlobalActions.java
index 6daf516..6eba59a 100644
--- a/services/core/java/com/android/server/policy/LegacyGlobalActions.java
+++ b/services/core/java/com/android/server/policy/LegacyGlobalActions.java
@@ -402,7 +402,7 @@
         public String getStatus() {
             return mContext.getString(
                     com.android.internal.R.string.bugreport_status,
-                    Build.VERSION.RELEASE,
+                    Build.VERSION.RELEASE_OR_CODENAME,
                     Build.ID);
         }
     }
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 96f1219..5c79f6e 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -2480,7 +2480,7 @@
                 .writeString(Build.BRAND)
                 .writeString(Build.PRODUCT)
                 .writeString(Build.DEVICE)
-                .writeString(Build.VERSION.RELEASE)
+                .writeString(Build.VERSION.RELEASE_OR_CODENAME)
                 .writeString(Build.ID)
                 .writeString(Build.VERSION.INCREMENTAL)
                 .writeString(Build.TYPE)
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index 0bb0f94..ed6424c 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -21,7 +21,7 @@
 import android.app.timedetector.ITimeDetectorService;
 import android.app.timedetector.ManualTimeSuggestion;
 import android.app.timedetector.NetworkTimeSuggestion;
-import android.app.timedetector.PhoneTimeSuggestion;
+import android.app.timedetector.TelephonyTimeSuggestion;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
@@ -94,11 +94,11 @@
     }
 
     @Override
-    public void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSignal) {
-        enforceSuggestPhoneTimePermission();
+    public void suggestTelephonyTime(@NonNull TelephonyTimeSuggestion timeSignal) {
+        enforceSuggestTelephonyTimePermission();
         Objects.requireNonNull(timeSignal);
 
-        mHandler.post(() -> mTimeDetectorStrategy.suggestPhoneTime(timeSignal));
+        mHandler.post(() -> mTimeDetectorStrategy.suggestTelephonyTime(timeSignal));
     }
 
     @Override
@@ -131,10 +131,10 @@
         mTimeDetectorStrategy.dump(pw, args);
     }
 
-    private void enforceSuggestPhoneTimePermission() {
+    private void enforceSuggestTelephonyTimePermission() {
         mContext.enforceCallingPermission(
-                android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE,
-                "suggest phone time and time zone");
+                android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE,
+                "suggest telephony time and time zone");
     }
 
     private void enforceSuggestManualTimePermission() {
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
index a7c3b4d..a5fba4e 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
@@ -20,7 +20,7 @@
 import android.annotation.Nullable;
 import android.app.timedetector.ManualTimeSuggestion;
 import android.app.timedetector.NetworkTimeSuggestion;
-import android.app.timedetector.PhoneTimeSuggestion;
+import android.app.timedetector.TelephonyTimeSuggestion;
 import android.os.TimestampedValue;
 
 import java.io.PrintWriter;
@@ -78,7 +78,7 @@
     void initialize(@NonNull Callback callback);
 
     /** Process the suggested time from telephony sources. */
-    void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion);
+    void suggestTelephonyTime(@NonNull TelephonyTimeSuggestion timeSuggestion);
 
     /** Process the suggested manually entered time. */
     void suggestManualTime(@NonNull ManualTimeSuggestion timeSuggestion);
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index 19435ee..8c54fa9 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -22,7 +22,7 @@
 import android.app.AlarmManager;
 import android.app.timedetector.ManualTimeSuggestion;
 import android.app.timedetector.NetworkTimeSuggestion;
-import android.app.timedetector.PhoneTimeSuggestion;
+import android.app.timedetector.TelephonyTimeSuggestion;
 import android.os.TimestampedValue;
 import android.util.LocalLog;
 import android.util.Slog;
@@ -38,9 +38,9 @@
 import java.lang.annotation.RetentionPolicy;
 
 /**
- * An implementation of {@link TimeDetectorStrategy} that passes phone and manual suggestions to
- * {@link AlarmManager}. When there are multiple phone sources, the one with the lowest ID is used
- * unless the data becomes too stale.
+ * An implementation of {@link TimeDetectorStrategy} that passes telephony and manual suggestions to
+ * {@link AlarmManager}. When there are multiple telephony sources, the one with the lowest ID is
+ * used unless the data becomes too stale.
  *
  * <p>Most public methods are marked synchronized to ensure thread safety around internal state.
  */
@@ -50,23 +50,26 @@
     private static final String LOG_TAG = "SimpleTimeDetectorStrategy";
 
     /** A score value used to indicate "no score", either due to validation failure or age. */
-    private static final int PHONE_INVALID_SCORE = -1;
-    /** The number of buckets phone suggestions can be put in by age. */
-    private static final int PHONE_BUCKET_COUNT = 24;
+    private static final int TELEPHONY_INVALID_SCORE = -1;
+    /** The number of buckets telephony suggestions can be put in by age. */
+    private static final int TELEPHONY_BUCKET_COUNT = 24;
     /** Each bucket is this size. All buckets are equally sized. */
     @VisibleForTesting
-    static final int PHONE_BUCKET_SIZE_MILLIS = 60 * 60 * 1000;
-    /** Phone and network suggestions older than this value are considered too old to be used. */
+    static final int TELEPHONY_BUCKET_SIZE_MILLIS = 60 * 60 * 1000;
+    /**
+     * Telephony and network suggestions older than this value are considered too old to be used.
+     */
     @VisibleForTesting
-    static final long MAX_UTC_TIME_AGE_MILLIS = PHONE_BUCKET_COUNT * PHONE_BUCKET_SIZE_MILLIS;
+    static final long MAX_UTC_TIME_AGE_MILLIS =
+            TELEPHONY_BUCKET_COUNT * TELEPHONY_BUCKET_SIZE_MILLIS;
 
-    @IntDef({ ORIGIN_PHONE, ORIGIN_MANUAL, ORIGIN_NETWORK })
+    @IntDef({ ORIGIN_TELEPHONY, ORIGIN_MANUAL, ORIGIN_NETWORK })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Origin {}
 
     /** Used when a time value originated from a telephony signal. */
     @Origin
-    private static final int ORIGIN_PHONE = 1;
+    private static final int ORIGIN_TELEPHONY = 1;
 
     /** Used when a time value originated from a user / manual settings. */
     @Origin
@@ -83,7 +86,9 @@
      */
     private static final long SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS = 2 * 1000;
 
-    /** The number of previous phone suggestions to keep for each ID (for use during debugging). */
+    /**
+     * The number of previous telephony suggestions to keep for each ID (for use during debugging).
+     */
     private static final int KEEP_SUGGESTION_HISTORY_SIZE = 30;
 
     // A log for changes made to the system clock and why.
@@ -106,7 +111,7 @@
      * stable.
      */
     @GuardedBy("this")
-    private final ArrayMapWithHistory<Integer, PhoneTimeSuggestion> mSuggestionBySlotIndex =
+    private final ArrayMapWithHistory<Integer, TelephonyTimeSuggestion> mSuggestionBySlotIndex =
             new ArrayMapWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
 
     @GuardedBy("this")
@@ -144,7 +149,7 @@
     }
 
     @Override
-    public synchronized void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) {
+    public synchronized void suggestTelephonyTime(@NonNull TelephonyTimeSuggestion timeSuggestion) {
         // Empty time suggestion means that telephony network connectivity has been lost.
         // The passage of time is relentless, and we don't expect our users to use a time machine,
         // so we can continue relying on previous suggestions when we lose connectivity. This is
@@ -157,13 +162,13 @@
 
         // Perform validation / input filtering and record the validated suggestion against the
         // slotIndex.
-        if (!validateAndStorePhoneSuggestion(timeSuggestion)) {
+        if (!validateAndStoreTelephonySuggestion(timeSuggestion)) {
             return;
         }
 
         // Now perform auto time detection. The new suggestion may be used to modify the system
         // clock.
-        String reason = "New phone time suggested. timeSuggestion=" + timeSuggestion;
+        String reason = "New telephony time suggested. timeSuggestion=" + timeSuggestion;
         doAutoTimeDetection(reason);
     }
 
@@ -201,7 +206,7 @@
         mTimeChangesLog.dump(ipw);
         ipw.decreaseIndent(); // level 2
 
-        ipw.println("Phone suggestion history:");
+        ipw.println("Telephony suggestion history:");
         ipw.increaseIndent(); // level 2
         mSuggestionBySlotIndex.dump(ipw);
         ipw.decreaseIndent(); // level 2
@@ -216,7 +221,8 @@
     }
 
     @GuardedBy("this")
-    private boolean validateAndStorePhoneSuggestion(@NonNull PhoneTimeSuggestion suggestion) {
+    private boolean validateAndStoreTelephonySuggestion(
+            @NonNull TelephonyTimeSuggestion suggestion) {
         TimestampedValue<Long> newUtcTime = suggestion.getUtcTime();
         if (!validateSuggestionTime(newUtcTime, suggestion)) {
             // There's probably nothing useful we can do: elsewhere we assume that reference
@@ -225,7 +231,7 @@
         }
 
         int slotIndex = suggestion.getSlotIndex();
-        PhoneTimeSuggestion previousSuggestion = mSuggestionBySlotIndex.get(slotIndex);
+        TelephonyTimeSuggestion previousSuggestion = mSuggestionBySlotIndex.get(slotIndex);
         if (previousSuggestion != null) {
             // We can log / discard suggestions with obvious issues with the reference time clock.
             if (previousSuggestion.getUtcTime() == null
@@ -241,7 +247,7 @@
                     newUtcTime, previousSuggestion.getUtcTime());
             if (referenceTimeDifference < 0) {
                 // The reference time is before the previously received suggestion. Ignore it.
-                Slog.w(LOG_TAG, "Out of order phone suggestion received."
+                Slog.w(LOG_TAG, "Out of order telephony suggestion received."
                         + " referenceTimeDifference=" + referenceTimeDifference
                         + " previousSuggestion=" + previousSuggestion
                         + " suggestion=" + suggestion);
@@ -282,18 +288,18 @@
 
         // Android devices currently prioritize any telephony over network signals. There are
         // carrier compliance tests that would need to be changed before we could ignore NITZ or
-        // prefer NTP generally. This check is cheap on devices without phone hardware.
-        PhoneTimeSuggestion bestPhoneSuggestion = findBestPhoneSuggestion();
-        if (bestPhoneSuggestion != null) {
-            final TimestampedValue<Long> newUtcTime = bestPhoneSuggestion.getUtcTime();
-            String cause = "Found good phone suggestion."
-                    + ", bestPhoneSuggestion=" + bestPhoneSuggestion
+        // prefer NTP generally. This check is cheap on devices without telephony hardware.
+        TelephonyTimeSuggestion bestTelephonySuggestion = findBestTelephonySuggestion();
+        if (bestTelephonySuggestion != null) {
+            final TimestampedValue<Long> newUtcTime = bestTelephonySuggestion.getUtcTime();
+            String cause = "Found good telephony suggestion."
+                    + ", bestTelephonySuggestion=" + bestTelephonySuggestion
                     + ", detectionReason=" + detectionReason;
-            setSystemClockIfRequired(ORIGIN_PHONE, newUtcTime, cause);
+            setSystemClockIfRequired(ORIGIN_TELEPHONY, newUtcTime, cause);
             return;
         }
 
-        // There is no good phone suggestion, try network.
+        // There is no good telephony suggestion, try network.
         NetworkTimeSuggestion networkSuggestion = findLatestValidNetworkSuggestion();
         if (networkSuggestion != null) {
             final TimestampedValue<Long> newUtcTime = networkSuggestion.getUtcTime();
@@ -305,18 +311,18 @@
         }
 
         if (DBG) {
-            Slog.d(LOG_TAG, "Could not determine time: No best phone or network suggestion."
+            Slog.d(LOG_TAG, "Could not determine time: No best telephony or network suggestion."
                     + " detectionReason=" + detectionReason);
         }
     }
 
     @GuardedBy("this")
     @Nullable
-    private PhoneTimeSuggestion findBestPhoneSuggestion() {
+    private TelephonyTimeSuggestion findBestTelephonySuggestion() {
         long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis();
 
-        // Phone time suggestions are assumed to be derived from NITZ or NITZ-like signals. These
-        // have a number of limitations:
+        // Telephony time suggestions are assumed to be derived from NITZ or NITZ-like signals.
+        // These have a number of limitations:
         // 1) No guarantee of accuracy ("accuracy of the time information is in the order of
         // minutes") [1]
         // 2) No guarantee of regular signals ("dependent on the handset crossing radio network
@@ -335,8 +341,8 @@
         // For simplicity, we try to value recency, then consistency of slotIndex.
         //
         // The heuristic works as follows:
-        // Recency: The most recent suggestion from each phone is scored. The score is based on a
-        // discrete age bucket, i.e. so signals received around the same time will be in the same
+        // Recency: The most recent suggestion from each slotIndex is scored. The score is based on
+        // a discrete age bucket, i.e. so signals received around the same time will be in the same
         // bucket, thus applying a loose reference time ordering. The suggestion with the highest
         // score is used.
         // Consistency: If there a multiple suggestions with the same score, the suggestion with the
@@ -345,11 +351,11 @@
         // In the trivial case with a single ID this will just mean that the latest received
         // suggestion is used.
 
-        PhoneTimeSuggestion bestSuggestion = null;
-        int bestScore = PHONE_INVALID_SCORE;
+        TelephonyTimeSuggestion bestSuggestion = null;
+        int bestScore = TELEPHONY_INVALID_SCORE;
         for (int i = 0; i < mSuggestionBySlotIndex.size(); i++) {
             Integer slotIndex = mSuggestionBySlotIndex.keyAt(i);
-            PhoneTimeSuggestion candidateSuggestion = mSuggestionBySlotIndex.valueAt(i);
+            TelephonyTimeSuggestion candidateSuggestion = mSuggestionBySlotIndex.valueAt(i);
             if (candidateSuggestion == null) {
                 // Unexpected - null suggestions should never be stored.
                 Slog.w(LOG_TAG, "Latest suggestion unexpectedly null for slotIndex."
@@ -362,8 +368,9 @@
                 continue;
             }
 
-            int candidateScore = scorePhoneSuggestion(elapsedRealtimeMillis, candidateSuggestion);
-            if (candidateScore == PHONE_INVALID_SCORE) {
+            int candidateScore =
+                    scoreTelephonySuggestion(elapsedRealtimeMillis, candidateSuggestion);
+            if (candidateScore == TELEPHONY_INVALID_SCORE) {
                 // Expected: This means the suggestion is obviously invalid or just too old.
                 continue;
             }
@@ -384,8 +391,8 @@
         return bestSuggestion;
     }
 
-    private static int scorePhoneSuggestion(
-            long elapsedRealtimeMillis, @NonNull PhoneTimeSuggestion timeSuggestion) {
+    private static int scoreTelephonySuggestion(
+            long elapsedRealtimeMillis, @NonNull TelephonyTimeSuggestion timeSuggestion) {
 
         // Validate first.
         TimestampedValue<Long> utcTime = timeSuggestion.getUtcTime();
@@ -393,21 +400,21 @@
             Slog.w(LOG_TAG, "Existing suggestion found to be invalid "
                     + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
                     + ", timeSuggestion=" + timeSuggestion);
-            return PHONE_INVALID_SCORE;
+            return TELEPHONY_INVALID_SCORE;
         }
 
         // The score is based on the age since receipt. Suggestions are bucketed so two
         // suggestions in the same bucket from different slotIndexs are scored the same.
         long ageMillis = elapsedRealtimeMillis - utcTime.getReferenceTimeMillis();
 
-        // Turn the age into a discrete value: 0 <= bucketIndex < PHONE_BUCKET_COUNT.
-        int bucketIndex = (int) (ageMillis / PHONE_BUCKET_SIZE_MILLIS);
-        if (bucketIndex >= PHONE_BUCKET_COUNT) {
-            return PHONE_INVALID_SCORE;
+        // Turn the age into a discrete value: 0 <= bucketIndex < TELEPHONY_BUCKET_COUNT.
+        int bucketIndex = (int) (ageMillis / TELEPHONY_BUCKET_SIZE_MILLIS);
+        if (bucketIndex >= TELEPHONY_BUCKET_COUNT) {
+            return TELEPHONY_INVALID_SCORE;
         }
 
         // We want the lowest bucket index to have the highest score. 0 > score >= BUCKET_COUNT.
-        return PHONE_BUCKET_COUNT - bucketIndex;
+        return TELEPHONY_BUCKET_COUNT - bucketIndex;
     }
 
     /** Returns the latest, valid, network suggestion. Returns {@code null} if there isn't one. */
@@ -537,13 +544,13 @@
     }
 
     /**
-     * Returns the current best phone suggestion. Not intended for general use: it is used during
-     * tests to check strategy behavior.
+     * Returns the current best telephony suggestion. Not intended for general use: it is used
+     * during tests to check strategy behavior.
      */
     @VisibleForTesting
     @Nullable
-    public synchronized PhoneTimeSuggestion findBestPhoneSuggestionForTests() {
-        return findBestPhoneSuggestion();
+    public synchronized TelephonyTimeSuggestion findBestTelephonySuggestionForTests() {
+        return findBestTelephonySuggestion();
     }
 
     /**
@@ -561,7 +568,7 @@
      */
     @VisibleForTesting
     @Nullable
-    public synchronized PhoneTimeSuggestion getLatestPhoneSuggestion(int slotIndex) {
+    public synchronized TelephonyTimeSuggestion getLatestTelephonySuggestion(int slotIndex) {
         return mSuggestionBySlotIndex.get(slotIndex);
     }
 
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index 381ee10..57b6ec9 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -20,7 +20,7 @@
 import android.annotation.Nullable;
 import android.app.timezonedetector.ITimeZoneDetectorService;
 import android.app.timezonedetector.ManualTimeZoneSuggestion;
-import android.app.timezonedetector.PhoneTimeZoneSuggestion;
+import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
@@ -101,11 +101,11 @@
     }
 
     @Override
-    public void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion timeZoneSuggestion) {
-        enforceSuggestPhoneTimeZonePermission();
+    public void suggestTelephonyTimeZone(@NonNull TelephonyTimeZoneSuggestion timeZoneSuggestion) {
+        enforceSuggestTelephonyTimeZonePermission();
         Objects.requireNonNull(timeZoneSuggestion);
 
-        mHandler.post(() -> mTimeZoneDetectorStrategy.suggestPhoneTimeZone(timeZoneSuggestion));
+        mHandler.post(() -> mTimeZoneDetectorStrategy.suggestTelephonyTimeZone(timeZoneSuggestion));
     }
 
     @Override
@@ -122,10 +122,10 @@
         mHandler.post(mTimeZoneDetectorStrategy::handleAutoTimeZoneDetectionChanged);
     }
 
-    private void enforceSuggestPhoneTimeZonePermission() {
+    private void enforceSuggestTelephonyTimeZonePermission() {
         mContext.enforceCallingPermission(
-                android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE,
-                "suggest phone time and time zone");
+                android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE,
+                "suggest telephony time and time zone");
     }
 
     private void enforceSuggestManualTimeZonePermission() {
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
index 1d439e9..0eb27cc 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
@@ -17,7 +17,7 @@
 
 import android.annotation.NonNull;
 import android.app.timezonedetector.ManualTimeZoneSuggestion;
-import android.app.timezonedetector.PhoneTimeZoneSuggestion;
+import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
 
 import java.io.PrintWriter;
 
@@ -38,13 +38,13 @@
 
     /**
      * Suggests a time zone for the device, or withdraws a previous suggestion if
-     * {@link PhoneTimeZoneSuggestion#getZoneId()} is {@code null}. The suggestion is scoped to a
-     * specific {@link PhoneTimeZoneSuggestion#getSlotIndex() phone}.
-     * See {@link PhoneTimeZoneSuggestion} for an explanation of the metadata associated with a
+     * {@link TelephonyTimeZoneSuggestion#getZoneId()} is {@code null}. The suggestion is scoped to
+     * a specific {@link TelephonyTimeZoneSuggestion#getSlotIndex() slotIndex}.
+     * See {@link TelephonyTimeZoneSuggestion} for an explanation of the metadata associated with a
      * suggestion. The strategy uses suggestions to decide whether to modify the device's time zone
      * setting and what to set it to.
      */
-    void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion suggestion);
+    void suggestTelephonyTimeZone(@NonNull TelephonyTimeZoneSuggestion suggestion);
 
     /**
      * Called when there has been a change to the automatic time zone detection setting.
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
index f85f9fe..652dbe15 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -15,17 +15,17 @@
  */
 package com.android.server.timezonedetector;
 
-import static android.app.timezonedetector.PhoneTimeZoneSuggestion.MATCH_TYPE_EMULATOR_ZONE_ID;
-import static android.app.timezonedetector.PhoneTimeZoneSuggestion.MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY;
-import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS;
-import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET;
-import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE;
+import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.MATCH_TYPE_EMULATOR_ZONE_ID;
+import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY;
+import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS;
+import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET;
+import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.timezonedetector.ManualTimeZoneSuggestion;
-import android.app.timezonedetector.PhoneTimeZoneSuggestion;
+import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
 import android.content.Context;
 import android.util.LocalLog;
 import android.util.Slog;
@@ -44,11 +44,11 @@
  * suggestions. Suggestions are acted on or ignored as needed, dependent on the current "auto time
  * zone detection" setting.
  *
- * <p>For automatic detection it keeps track of the most recent suggestion from each phone it uses
- * the best suggestion based on a scoring algorithm. If several phones provide the same score then
- * the phone with the lowest numeric ID "wins". If the situation changes and it is no longer
- * possible to be confident about the time zone, phones must submit an empty suggestion in order to
- * "withdraw" their previous suggestion.
+ * <p>For automatic detection, it keeps track of the most recent telephony suggestion from each
+ * slotIndex and it uses the best suggestion based on a scoring algorithm. If several slotIndexes
+ * provide the same score then the slotIndex with the lowest numeric value "wins". If the situation
+ * changes and it is no longer possible to be confident about the time zone, slotIndexes must have
+ * an empty suggestion submitted in order to "withdraw" their previous suggestion.
  *
  * <p>Most public methods are marked synchronized to ensure thread safety around internal state.
  */
@@ -91,28 +91,28 @@
     private static final String LOG_TAG = "TimeZoneDetectorStrategy";
     private static final boolean DBG = false;
 
-    @IntDef({ ORIGIN_PHONE, ORIGIN_MANUAL })
+    @IntDef({ ORIGIN_TELEPHONY, ORIGIN_MANUAL })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Origin {}
 
     /** Used when a time value originated from a telephony signal. */
     @Origin
-    private static final int ORIGIN_PHONE = 1;
+    private static final int ORIGIN_TELEPHONY = 1;
 
     /** Used when a time value originated from a user / manual settings. */
     @Origin
     private static final int ORIGIN_MANUAL = 2;
 
     /**
-     * The abstract score for an empty or invalid phone suggestion.
+     * The abstract score for an empty or invalid telephony suggestion.
      *
-     * Used to score phone suggestions where there is no zone.
+     * Used to score telephony suggestions where there is no zone.
      */
     @VisibleForTesting
-    public static final int PHONE_SCORE_NONE = 0;
+    public static final int TELEPHONY_SCORE_NONE = 0;
 
     /**
-     * The abstract score for a low quality phone suggestion.
+     * The abstract score for a low quality telephony suggestion.
      *
      * Used to score suggestions where:
      * The suggested zone ID is one of several possibilities, and the possibilities have different
@@ -121,10 +121,10 @@
      * You would have to be quite desperate to want to use this choice.
      */
     @VisibleForTesting
-    public static final int PHONE_SCORE_LOW = 1;
+    public static final int TELEPHONY_SCORE_LOW = 1;
 
     /**
-     * The abstract score for a medium quality phone suggestion.
+     * The abstract score for a medium quality telephony suggestion.
      *
      * Used for:
      * The suggested zone ID is one of several possibilities but at least the possibilities have the
@@ -132,36 +132,38 @@
      * switch to DST at the wrong time and (for example) their calendar events.
      */
     @VisibleForTesting
-    public static final int PHONE_SCORE_MEDIUM = 2;
+    public static final int TELEPHONY_SCORE_MEDIUM = 2;
 
     /**
-     * The abstract score for a high quality phone suggestion.
+     * The abstract score for a high quality telephony suggestion.
      *
      * Used for:
      * The suggestion was for one zone ID and the answer was unambiguous and likely correct given
      * the info available.
      */
     @VisibleForTesting
-    public static final int PHONE_SCORE_HIGH = 3;
+    public static final int TELEPHONY_SCORE_HIGH = 3;
 
     /**
-     * The abstract score for a highest quality phone suggestion.
+     * The abstract score for a highest quality telephony suggestion.
      *
      * Used for:
      * Suggestions that must "win" because they constitute test or emulator zone ID.
      */
     @VisibleForTesting
-    public static final int PHONE_SCORE_HIGHEST = 4;
+    public static final int TELEPHONY_SCORE_HIGHEST = 4;
 
     /**
-     * The threshold at which phone suggestions are good enough to use to set the device's time
+     * The threshold at which telephony suggestions are good enough to use to set the device's time
      * zone.
      */
     @VisibleForTesting
-    public static final int PHONE_SCORE_USAGE_THRESHOLD = PHONE_SCORE_MEDIUM;
+    public static final int TELEPHONY_SCORE_USAGE_THRESHOLD = TELEPHONY_SCORE_MEDIUM;
 
-    /** The number of previous phone suggestions to keep for each ID (for use during debugging). */
-    private static final int KEEP_PHONE_SUGGESTION_HISTORY_SIZE = 30;
+    /**
+     * The number of previous telephony suggestions to keep for each ID (for use during debugging).
+     */
+    private static final int KEEP_TELEPHONY_SUGGESTION_HISTORY_SIZE = 30;
 
     @NonNull
     private final Callback mCallback;
@@ -174,13 +176,14 @@
     private final LocalLog mTimeZoneChangesLog = new LocalLog(30, false /* useLocalTimestamps */);
 
     /**
-     * A mapping from slotIndex to a phone time zone suggestion. We typically expect one or two
-     * mappings: devices will have a small number of telephony devices and slotIndexs are assumed to
-     * be stable.
+     * A mapping from slotIndex to a telephony time zone suggestion. We typically expect one or two
+     * mappings: devices will have a small number of telephony devices and slotIndexes are assumed
+     * to be stable.
      */
     @GuardedBy("this")
-    private ArrayMapWithHistory<Integer, QualifiedPhoneTimeZoneSuggestion> mSuggestionBySlotIndex =
-            new ArrayMapWithHistory<>(KEEP_PHONE_SUGGESTION_HISTORY_SIZE);
+    private ArrayMapWithHistory<Integer, QualifiedTelephonyTimeZoneSuggestion>
+            mSuggestionBySlotIndex =
+            new ArrayMapWithHistory<>(KEEP_TELEPHONY_SUGGESTION_HISTORY_SIZE);
 
     /**
      * Creates a new instance of {@link TimeZoneDetectorStrategyImpl}.
@@ -205,42 +208,43 @@
     }
 
     @Override
-    public synchronized void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion suggestion) {
+    public synchronized void suggestTelephonyTimeZone(
+            @NonNull TelephonyTimeZoneSuggestion suggestion) {
         if (DBG) {
-            Slog.d(LOG_TAG, "Phone suggestion received. newSuggestion=" + suggestion);
+            Slog.d(LOG_TAG, "Telephony suggestion received. newSuggestion=" + suggestion);
         }
         Objects.requireNonNull(suggestion);
 
         // Score the suggestion.
-        int score = scorePhoneSuggestion(suggestion);
-        QualifiedPhoneTimeZoneSuggestion scoredSuggestion =
-                new QualifiedPhoneTimeZoneSuggestion(suggestion, score);
+        int score = scoreTelephonySuggestion(suggestion);
+        QualifiedTelephonyTimeZoneSuggestion scoredSuggestion =
+                new QualifiedTelephonyTimeZoneSuggestion(suggestion, score);
 
         // Store the suggestion against the correct slotIndex.
         mSuggestionBySlotIndex.put(suggestion.getSlotIndex(), scoredSuggestion);
 
         // Now perform auto time zone detection. The new suggestion may be used to modify the time
         // zone setting.
-        String reason = "New phone time suggested. suggestion=" + suggestion;
+        String reason = "New telephony time suggested. suggestion=" + suggestion;
         doAutoTimeZoneDetection(reason);
     }
 
-    private static int scorePhoneSuggestion(@NonNull PhoneTimeZoneSuggestion suggestion) {
+    private static int scoreTelephonySuggestion(@NonNull TelephonyTimeZoneSuggestion suggestion) {
         int score;
         if (suggestion.getZoneId() == null) {
-            score = PHONE_SCORE_NONE;
+            score = TELEPHONY_SCORE_NONE;
         } else if (suggestion.getMatchType() == MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY
                 || suggestion.getMatchType() == MATCH_TYPE_EMULATOR_ZONE_ID) {
             // Handle emulator / test cases : These suggestions should always just be used.
-            score = PHONE_SCORE_HIGHEST;
+            score = TELEPHONY_SCORE_HIGHEST;
         } else if (suggestion.getQuality() == QUALITY_SINGLE_ZONE) {
-            score = PHONE_SCORE_HIGH;
+            score = TELEPHONY_SCORE_HIGH;
         } else if (suggestion.getQuality() == QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET) {
             // The suggestion may be wrong, but at least the offset should be correct.
-            score = PHONE_SCORE_MEDIUM;
+            score = TELEPHONY_SCORE_MEDIUM;
         } else if (suggestion.getQuality() == QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS) {
             // The suggestion has a good chance of being wrong.
-            score = PHONE_SCORE_LOW;
+            score = TELEPHONY_SCORE_LOW;
         } else {
             throw new AssertionError();
         }
@@ -248,9 +252,9 @@
     }
 
     /**
-     * Finds the best available time zone suggestion from all phones. If it is high-enough quality
-     * and automatic time zone detection is enabled then it will be set on the device. The outcome
-     * can be that this strategy becomes / remains un-opinionated and nothing is set.
+     * Finds the best available time zone suggestion from all slotIndexes. If it is high-enough
+     * quality and automatic time zone detection is enabled then it will be set on the device. The
+     * outcome can be that this strategy becomes / remains un-opinionated and nothing is set.
      */
     @GuardedBy("this")
     private void doAutoTimeZoneDetection(@NonNull String detectionReason) {
@@ -259,35 +263,37 @@
             return;
         }
 
-        QualifiedPhoneTimeZoneSuggestion bestPhoneSuggestion = findBestPhoneSuggestion();
+        QualifiedTelephonyTimeZoneSuggestion bestTelephonySuggestion =
+                findBestTelephonySuggestion();
 
         // Work out what to do with the best suggestion.
-        if (bestPhoneSuggestion == null) {
-            // There is no phone suggestion available at all. Become un-opinionated.
+        if (bestTelephonySuggestion == null) {
+            // There is no telephony suggestion available at all. Become un-opinionated.
             if (DBG) {
-                Slog.d(LOG_TAG, "Could not determine time zone: No best phone suggestion."
+                Slog.d(LOG_TAG, "Could not determine time zone: No best telephony suggestion."
                         + " detectionReason=" + detectionReason);
             }
             return;
         }
 
         // Special case handling for uninitialized devices. This should only happen once.
-        String newZoneId = bestPhoneSuggestion.suggestion.getZoneId();
+        String newZoneId = bestTelephonySuggestion.suggestion.getZoneId();
         if (newZoneId != null && !mCallback.isDeviceTimeZoneInitialized()) {
             String cause = "Device has no time zone set. Attempting to set the device to the best"
                     + " available suggestion."
-                    + " bestPhoneSuggestion=" + bestPhoneSuggestion
+                    + " bestTelephonySuggestion=" + bestTelephonySuggestion
                     + ", detectionReason=" + detectionReason;
             Slog.i(LOG_TAG, cause);
-            setDeviceTimeZoneIfRequired(ORIGIN_PHONE, newZoneId, cause);
+            setDeviceTimeZoneIfRequired(ORIGIN_TELEPHONY, newZoneId, cause);
             return;
         }
 
-        boolean suggestionGoodEnough = bestPhoneSuggestion.score >= PHONE_SCORE_USAGE_THRESHOLD;
+        boolean suggestionGoodEnough =
+                bestTelephonySuggestion.score >= TELEPHONY_SCORE_USAGE_THRESHOLD;
         if (!suggestionGoodEnough) {
             if (DBG) {
                 Slog.d(LOG_TAG, "Best suggestion not good enough."
-                        + " bestPhoneSuggestion=" + bestPhoneSuggestion
+                        + " bestTelephonySuggestion=" + bestTelephonySuggestion
                         + ", detectionReason=" + detectionReason);
             }
             return;
@@ -297,16 +303,16 @@
         // zone ID.
         if (newZoneId == null) {
             Slog.w(LOG_TAG, "Empty zone suggestion scored higher than expected. This is an error:"
-                    + " bestPhoneSuggestion=" + bestPhoneSuggestion
+                    + " bestTelephonySuggestion=" + bestTelephonySuggestion
                     + " detectionReason=" + detectionReason);
             return;
         }
 
-        String zoneId = bestPhoneSuggestion.suggestion.getZoneId();
+        String zoneId = bestTelephonySuggestion.suggestion.getZoneId();
         String cause = "Found good suggestion."
-                + ", bestPhoneSuggestion=" + bestPhoneSuggestion
+                + ", bestTelephonySuggestion=" + bestTelephonySuggestion
                 + ", detectionReason=" + detectionReason;
-        setDeviceTimeZoneIfRequired(ORIGIN_PHONE, zoneId, cause);
+        setDeviceTimeZoneIfRequired(ORIGIN_TELEPHONY, zoneId, cause);
     }
 
     @GuardedBy("this")
@@ -372,15 +378,15 @@
 
     @GuardedBy("this")
     @Nullable
-    private QualifiedPhoneTimeZoneSuggestion findBestPhoneSuggestion() {
-        QualifiedPhoneTimeZoneSuggestion bestSuggestion = null;
+    private QualifiedTelephonyTimeZoneSuggestion findBestTelephonySuggestion() {
+        QualifiedTelephonyTimeZoneSuggestion bestSuggestion = null;
 
-        // Iterate over the latest QualifiedPhoneTimeZoneSuggestion objects received for each phone
-        // and find the best. Note that we deliberately do not look at age: the caller can
+        // Iterate over the latest QualifiedTelephonyTimeZoneSuggestion objects received for each
+        // slotIndex and find the best. Note that we deliberately do not look at age: the caller can
         // rate-limit so age is not a strong indicator of confidence. Instead, the callers are
         // expected to withdraw suggestions they no longer have confidence in.
         for (int i = 0; i < mSuggestionBySlotIndex.size(); i++) {
-            QualifiedPhoneTimeZoneSuggestion candidateSuggestion =
+            QualifiedTelephonyTimeZoneSuggestion candidateSuggestion =
                     mSuggestionBySlotIndex.valueAt(i);
             if (candidateSuggestion == null) {
                 // Unexpected
@@ -404,13 +410,13 @@
     }
 
     /**
-     * Returns the current best phone suggestion. Not intended for general use: it is used during
-     * tests to check strategy behavior.
+     * Returns the current best telephony suggestion. Not intended for general use: it is used
+     * during tests to check strategy behavior.
      */
     @VisibleForTesting
     @Nullable
-    public synchronized QualifiedPhoneTimeZoneSuggestion findBestPhoneSuggestionForTests() {
-        return findBestPhoneSuggestion();
+    public synchronized QualifiedTelephonyTimeZoneSuggestion findBestTelephonySuggestionForTests() {
+        return findBestTelephonySuggestion();
     }
 
     @Override
@@ -447,7 +453,7 @@
         mTimeZoneChangesLog.dump(ipw);
         ipw.decreaseIndent(); // level 2
 
-        ipw.println("Phone suggestion history:");
+        ipw.println("Telephony suggestion history:");
         ipw.increaseIndent(); // level 2
         mSuggestionBySlotIndex.dump(ipw);
         ipw.decreaseIndent(); // level 2
@@ -459,18 +465,19 @@
      * A method used to inspect strategy state during tests. Not intended for general use.
      */
     @VisibleForTesting
-    public synchronized QualifiedPhoneTimeZoneSuggestion getLatestPhoneSuggestion(int slotIndex) {
+    public synchronized QualifiedTelephonyTimeZoneSuggestion getLatestTelephonySuggestion(
+            int slotIndex) {
         return mSuggestionBySlotIndex.get(slotIndex);
     }
 
     /**
-     * A {@link PhoneTimeZoneSuggestion} with additional qualifying metadata.
+     * A {@link TelephonyTimeZoneSuggestion} with additional qualifying metadata.
      */
     @VisibleForTesting
-    public static class QualifiedPhoneTimeZoneSuggestion {
+    public static class QualifiedTelephonyTimeZoneSuggestion {
 
         @VisibleForTesting
-        public final PhoneTimeZoneSuggestion suggestion;
+        public final TelephonyTimeZoneSuggestion suggestion;
 
         /**
          * The score the suggestion has been given. This can be used to rank against other
@@ -480,7 +487,8 @@
         public final int score;
 
         @VisibleForTesting
-        public QualifiedPhoneTimeZoneSuggestion(PhoneTimeZoneSuggestion suggestion, int score) {
+        public QualifiedTelephonyTimeZoneSuggestion(
+                TelephonyTimeZoneSuggestion suggestion, int score) {
             this.suggestion = suggestion;
             this.score = score;
         }
@@ -493,7 +501,7 @@
             if (o == null || getClass() != o.getClass()) {
                 return false;
             }
-            QualifiedPhoneTimeZoneSuggestion that = (QualifiedPhoneTimeZoneSuggestion) o;
+            QualifiedTelephonyTimeZoneSuggestion that = (QualifiedTelephonyTimeZoneSuggestion) o;
             return score == that.score
                     && suggestion.equals(that.suggestion);
         }
@@ -505,7 +513,7 @@
 
         @Override
         public String toString() {
-            return "QualifiedPhoneTimeZoneSuggestion{"
+            return "QualifiedTelephonyTimeZoneSuggestion{"
                     + "suggestion=" + suggestion
                     + ", score=" + score
                     + '}';
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 4f010d5..e3b1152c 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -383,36 +383,38 @@
             if (mCurrentUserId == userId) {
                 return;
             }
-            UserState userState = mUserStates.get(mCurrentUserId);
-            List<SessionState> sessionStatesToRelease = new ArrayList<>();
-            for (SessionState sessionState : userState.sessionStateMap.values()) {
-                if (sessionState.session != null && !sessionState.isRecordingSession) {
-                    sessionStatesToRelease.add(sessionState);
-                }
-            }
-            for (SessionState sessionState : sessionStatesToRelease) {
-                try {
-                    sessionState.session.release();
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "error in release", e);
-                }
-                clearSessionAndNotifyClientLocked(sessionState);
-            }
-
-            for (Iterator<ComponentName> it = userState.serviceStateMap.keySet().iterator();
-                 it.hasNext(); ) {
-                ComponentName component = it.next();
-                ServiceState serviceState = userState.serviceStateMap.get(component);
-                if (serviceState != null && serviceState.sessionTokens.isEmpty()) {
-                    if (serviceState.callback != null) {
-                        try {
-                            serviceState.service.unregisterCallback(serviceState.callback);
-                        } catch (RemoteException e) {
-                            Slog.e(TAG, "error in unregisterCallback", e);
-                        }
+            if (mUserStates.contains(mCurrentUserId)) {
+                UserState userState = mUserStates.get(mCurrentUserId);
+                List<SessionState> sessionStatesToRelease = new ArrayList<>();
+                for (SessionState sessionState : userState.sessionStateMap.values()) {
+                    if (sessionState.session != null && !sessionState.isRecordingSession) {
+                        sessionStatesToRelease.add(sessionState);
                     }
-                    mContext.unbindService(serviceState.connection);
-                    it.remove();
+                }
+                for (SessionState sessionState : sessionStatesToRelease) {
+                    try {
+                        sessionState.session.release();
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "error in release", e);
+                    }
+                    clearSessionAndNotifyClientLocked(sessionState);
+                }
+
+                for (Iterator<ComponentName> it = userState.serviceStateMap.keySet().iterator();
+                    it.hasNext(); ) {
+                    ComponentName component = it.next();
+                    ServiceState serviceState = userState.serviceStateMap.get(component);
+                    if (serviceState != null && serviceState.sessionTokens.isEmpty()) {
+                        if (serviceState.callback != null) {
+                            try {
+                                serviceState.service.unregisterCallback(serviceState.callback);
+                            } catch (RemoteException e) {
+                                Slog.e(TAG, "error in unregisterCallback", e);
+                            }
+                        }
+                        mContext.unbindService(serviceState.connection);
+                        it.remove();
+                    }
                 }
             }
 
@@ -490,6 +492,10 @@
             userState.mainSessionToken = null;
 
             mUserStates.remove(userId);
+
+            if (userId == mCurrentUserId) {
+                switchUser(UserHandle.USER_SYSTEM);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 75d87ed..f35ba9e 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -386,6 +386,8 @@
         } else {
             callingPid = callingUid = -1;
         }
+        final int filterCallingUid = ActivityStarter.computeResolveFilterUid(
+                callingUid, realCallingUid, UserHandle.USER_NULL);
         final SparseArray<String> startingUidPkgs = new SparseArray<>();
         final long origId = Binder.clearCallingIdentity();
         try {
@@ -408,9 +410,7 @@
 
                 // Collect information about the target of the Intent.
                 ActivityInfo aInfo = mSupervisor.resolveActivity(intent, resolvedTypes[i],
-                        0 /* startFlags */, null /* profilerInfo */, userId,
-                        ActivityStarter.computeResolveFilterUid(
-                                callingUid, realCallingUid, UserHandle.USER_NULL));
+                        0 /* startFlags */, null /* profilerInfo */, userId, filterCallingUid);
                 aInfo = mService.mAmInternal.getActivityInfoForUser(aInfo, userId);
 
                 if (aInfo != null) {
@@ -457,6 +457,7 @@
                 Slog.wtf(TAG, sb.toString());
             }
 
+            final IBinder sourceResultTo = resultTo;
             final ActivityRecord[] outActivity = new ActivityRecord[1];
             // Lock the loop to ensure the activities launched in a sequence.
             synchronized (mService.mGlobalLock) {
@@ -470,7 +471,18 @@
                         }
                         return startResult;
                     }
-                    resultTo = outActivity[0] != null ? outActivity[0].appToken : null;
+                    final ActivityRecord started = outActivity[0];
+                    if (started != null && started.getUid() == filterCallingUid) {
+                        // Only the started activity which has the same uid as the source caller can
+                        // be the caller of next activity.
+                        resultTo = started.appToken;
+                    } else {
+                        resultTo = sourceResultTo;
+                        // Different apps not adjacent to the caller are forced to be new task.
+                        if (i < starters.length - 1) {
+                            starters[i + 1].getIntent().addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                        }
+                    }
                 }
             }
         } finally {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 9e3292b..c7270f2 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2493,7 +2493,6 @@
         return this;
     }
 
-    @VisibleForTesting
     Intent getIntent() {
         return mRequest.intent;
     }
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 3d9f11f..339ff6b 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -22,6 +22,7 @@
         "services.net",
         "service-jobscheduler",
         "service-permission",
+        "service-blobstore",
         "androidx.test.runner",
         "mockito-target-extended-minus-junit4",
         "platform-test-annotations",
diff --git a/services/tests/mockingservicestests/AndroidManifest.xml b/services/tests/mockingservicestests/AndroidManifest.xml
index 0e24b03..44eb828 100644
--- a/services/tests/mockingservicestests/AndroidManifest.xml
+++ b/services/tests/mockingservicestests/AndroidManifest.xml
@@ -17,6 +17,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.frameworks.mockingservicestests">
 
+    <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/>
+    <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/>
     <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
     <uses-permission android:name="android.permission.HARDWARE_TEST"/>
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
diff --git a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
new file mode 100644
index 0000000..16dde42
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright 2020 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.server.blob;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.server.blob.BlobStoreConfig.SESSION_EXPIRY_TIMEOUT_MILLIS;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.app.blob.BlobHandle;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+import android.util.ArrayMap;
+import android.util.LongSparseArray;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.blob.BlobStoreManagerService.Injector;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+import java.io.File;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class BlobStoreManagerServiceTest {
+    private Context mContext;
+    private Handler mHandler;
+    private BlobStoreManagerService mService;
+
+    private MockitoSession mMockitoSession;
+
+    @Mock
+    private File mBlobsDir;
+
+    private LongSparseArray<BlobStoreSession> mUserSessions;
+    private ArrayMap<BlobHandle, BlobMetadata> mUserBlobs;
+
+    private static final String TEST_PKG1 = "com.example1";
+    private static final String TEST_PKG2 = "com.example2";
+    private static final String TEST_PKG3 = "com.example3";
+
+    private static final int TEST_UID1 = 10001;
+    private static final int TEST_UID2 = 10002;
+    private static final int TEST_UID3 = 10003;
+
+    @Before
+    public void setUp() {
+        // Share classloader to allow package private access.
+        System.setProperty("dexmaker.share_classloader", "true");
+
+        mMockitoSession = mockitoSession()
+                .initMocks(this)
+                .strictness(Strictness.LENIENT)
+                .mockStatic(BlobStoreConfig.class)
+                .startMocking();
+
+        doReturn(mBlobsDir).when(() -> BlobStoreConfig.getBlobsDir());
+        doReturn(true).when(mBlobsDir).exists();
+        doReturn(new File[0]).when(mBlobsDir).listFiles();
+
+        mContext = InstrumentationRegistry.getTargetContext();
+        mHandler = new TestHandler(Looper.getMainLooper());
+        mService = new BlobStoreManagerService(mContext, new TestInjector());
+        mUserSessions = new LongSparseArray<>();
+        mUserBlobs = new ArrayMap<>();
+
+        mService.addUserSessionsForTest(mUserSessions, UserHandle.myUserId());
+        mService.addUserBlobsForTest(mUserBlobs, UserHandle.myUserId());
+    }
+
+    @After
+    public void tearDown() {
+        if (mMockitoSession != null) {
+            mMockitoSession.finishMocking();
+        }
+    }
+
+    @Test
+    public void testHandlePackageRemoved() throws Exception {
+        // Setup sessions
+        final File sessionFile1 = mock(File.class);
+        final long sessionId1 = 11;
+        final BlobStoreSession session1 = createBlobStoreSessionMock(TEST_PKG1, TEST_UID1,
+                sessionId1, sessionFile1);
+        mUserSessions.append(sessionId1, session1);
+
+        final File sessionFile2 = mock(File.class);
+        final long sessionId2 = 25;
+        final BlobStoreSession session2 = createBlobStoreSessionMock(TEST_PKG2, TEST_UID2,
+                sessionId2, sessionFile2);
+        mUserSessions.append(sessionId2, session2);
+
+        final File sessionFile3 = mock(File.class);
+        final long sessionId3 = 37;
+        final BlobStoreSession session3 = createBlobStoreSessionMock(TEST_PKG3, TEST_UID3,
+                sessionId3, sessionFile3);
+        mUserSessions.append(sessionId3, session3);
+
+        final File sessionFile4 = mock(File.class);
+        final long sessionId4 = 48;
+        final BlobStoreSession session4 = createBlobStoreSessionMock(TEST_PKG1, TEST_UID1,
+                sessionId4, sessionFile4);
+        mUserSessions.append(sessionId4, session4);
+
+        // Setup blobs
+        final long blobId1 = 978;
+        final File blobFile1 = mock(File.class);
+        final BlobHandle blobHandle1 = BlobHandle.createWithSha256("digest1".getBytes(),
+                "label1", System.currentTimeMillis(), "tag1");
+        final BlobMetadata blobMetadata1 = createBlobMetadataMock(blobId1, blobFile1, true);
+        mUserBlobs.put(blobHandle1, blobMetadata1);
+
+        final long blobId2 = 347;
+        final File blobFile2 = mock(File.class);
+        final BlobHandle blobHandle2 = BlobHandle.createWithSha256("digest2".getBytes(),
+                "label2", System.currentTimeMillis(), "tag2");
+        final BlobMetadata blobMetadata2 = createBlobMetadataMock(blobId2, blobFile2, false);
+        mUserBlobs.put(blobHandle2, blobMetadata2);
+
+        mService.addKnownIdsForTest(sessionId1, sessionId2, sessionId3, sessionId4,
+                blobId1, blobId2);
+
+        // Invoke test method
+        mService.handlePackageRemoved(TEST_PKG1, TEST_UID1);
+
+        // Verify sessions are removed
+        verify(sessionFile1).delete();
+        verify(sessionFile2, never()).delete();
+        verify(sessionFile3, never()).delete();
+        verify(sessionFile4).delete();
+
+        assertThat(mUserSessions.size()).isEqualTo(2);
+        assertThat(mUserSessions.get(sessionId1)).isNull();
+        assertThat(mUserSessions.get(sessionId2)).isNotNull();
+        assertThat(mUserSessions.get(sessionId3)).isNotNull();
+        assertThat(mUserSessions.get(sessionId4)).isNull();
+
+        // Verify blobs are removed
+        verify(blobMetadata1).removeCommitter(TEST_PKG1, TEST_UID1);
+        verify(blobMetadata1).removeLeasee(TEST_PKG1, TEST_UID1);
+        verify(blobMetadata2).removeCommitter(TEST_PKG1, TEST_UID1);
+        verify(blobMetadata2).removeLeasee(TEST_PKG1, TEST_UID1);
+
+        verify(blobFile1, never()).delete();
+        verify(blobFile2).delete();
+
+        assertThat(mUserBlobs.size()).isEqualTo(1);
+        assertThat(mUserBlobs.get(blobHandle1)).isNotNull();
+        assertThat(mUserBlobs.get(blobHandle2)).isNull();
+
+        assertThat(mService.getKnownIdsForTest()).containsExactly(
+                sessionId2, sessionId3, blobId1);
+    }
+
+    @Test
+    public void testHandleIdleMaintenance_deleteUnknownBlobs() throws Exception {
+        // Setup blob files
+        final long testId1 = 286;
+        final File file1 = mock(File.class);
+        doReturn(String.valueOf(testId1)).when(file1).getName();
+        final long testId2 = 349;
+        final File file2 = mock(File.class);
+        doReturn(String.valueOf(testId2)).when(file2).getName();
+        final long testId3 = 7355;
+        final File file3 = mock(File.class);
+        doReturn(String.valueOf(testId3)).when(file3).getName();
+
+        doReturn(new File[] {file1, file2, file3}).when(mBlobsDir).listFiles();
+        mService.addKnownIdsForTest(testId1, testId3);
+
+        // Invoke test method
+        mService.handleIdleMaintenanceLocked();
+
+        // Verify unknown blobs are delete
+        verify(file1, never()).delete();
+        verify(file2).delete();
+        verify(file3, never()).delete();
+    }
+
+    @Test
+    public void testHandleIdleMaintenance_deleteStaleSessions() throws Exception {
+        // Setup sessions
+        final File sessionFile1 = mock(File.class);
+        doReturn(System.currentTimeMillis() - SESSION_EXPIRY_TIMEOUT_MILLIS + 1000)
+                .when(sessionFile1).lastModified();
+        final long sessionId1 = 342;
+        final BlobHandle blobHandle1 = mock(BlobHandle.class);
+        doReturn(System.currentTimeMillis() - 1000).when(blobHandle1).getExpiryTimeMillis();
+        final BlobStoreSession session1 = createBlobStoreSessionMock(TEST_PKG1, TEST_UID1,
+                sessionId1, sessionFile1, blobHandle1);
+        mUserSessions.append(sessionId1, session1);
+
+        final File sessionFile2 = mock(File.class);
+        doReturn(System.currentTimeMillis() - 20000)
+                .when(sessionFile2).lastModified();
+        final long sessionId2 = 4597;
+        final BlobHandle blobHandle2 = mock(BlobHandle.class);
+        doReturn(System.currentTimeMillis() + 20000).when(blobHandle2).getExpiryTimeMillis();
+        final BlobStoreSession session2 = createBlobStoreSessionMock(TEST_PKG2, TEST_UID2,
+                sessionId2, sessionFile2, blobHandle2);
+        mUserSessions.append(sessionId2, session2);
+
+        final File sessionFile3 = mock(File.class);
+        doReturn(System.currentTimeMillis() - SESSION_EXPIRY_TIMEOUT_MILLIS - 2000)
+                .when(sessionFile3).lastModified();
+        final long sessionId3 = 9484;
+        final BlobHandle blobHandle3 = mock(BlobHandle.class);
+        doReturn(System.currentTimeMillis() + 30000).when(blobHandle3).getExpiryTimeMillis();
+        final BlobStoreSession session3 = createBlobStoreSessionMock(TEST_PKG3, TEST_UID3,
+                sessionId3, sessionFile3, blobHandle3);
+        mUserSessions.append(sessionId3, session3);
+
+        mService.addKnownIdsForTest(sessionId1, sessionId2, sessionId3);
+
+        // Invoke test method
+        mService.handleIdleMaintenanceLocked();
+
+        // Verify stale sessions are removed
+        verify(sessionFile1).delete();
+        verify(sessionFile2, never()).delete();
+        verify(sessionFile3).delete();
+
+        assertThat(mUserSessions.size()).isEqualTo(1);
+        assertThat(mUserSessions.get(sessionId2)).isNotNull();
+
+        assertThat(mService.getKnownIdsForTest()).containsExactly(sessionId2);
+    }
+
+    @Test
+    public void testHandleIdleMaintenance_deleteStaleBlobs() throws Exception {
+        // Setup blobs
+        final long blobId1 = 3489;
+        final File blobFile1 = mock(File.class);
+        final BlobHandle blobHandle1 = BlobHandle.createWithSha256("digest1".getBytes(),
+                "label1", System.currentTimeMillis() - 2000, "tag1");
+        final BlobMetadata blobMetadata1 = createBlobMetadataMock(blobId1, blobFile1, true);
+        mUserBlobs.put(blobHandle1, blobMetadata1);
+
+        final long blobId2 = 78974;
+        final File blobFile2 = mock(File.class);
+        final BlobHandle blobHandle2 = BlobHandle.createWithSha256("digest2".getBytes(),
+                "label2", System.currentTimeMillis() + 30000, "tag2");
+        final BlobMetadata blobMetadata2 = createBlobMetadataMock(blobId2, blobFile2, true);
+        mUserBlobs.put(blobHandle2, blobMetadata2);
+
+        final long blobId3 = 97;
+        final File blobFile3 = mock(File.class);
+        final BlobHandle blobHandle3 = BlobHandle.createWithSha256("digest3".getBytes(),
+                "label3", System.currentTimeMillis() + 4400000, "tag3");
+        final BlobMetadata blobMetadata3 = createBlobMetadataMock(blobId3, blobFile3, false);
+        mUserBlobs.put(blobHandle3, blobMetadata3);
+
+        mService.addKnownIdsForTest(blobId1, blobId2, blobId3);
+
+        // Invoke test method
+        mService.handleIdleMaintenanceLocked();
+
+        // Verify stale blobs are removed
+        verify(blobFile1).delete();
+        verify(blobFile2, never()).delete();
+        verify(blobFile3).delete();
+
+        assertThat(mUserBlobs.size()).isEqualTo(1);
+        assertThat(mUserBlobs.get(blobHandle2)).isNotNull();
+
+        assertThat(mService.getKnownIdsForTest()).containsExactly(blobId2);
+    }
+
+    private BlobStoreSession createBlobStoreSessionMock(String ownerPackageName, int ownerUid,
+            long sessionId, File sessionFile) {
+        return createBlobStoreSessionMock(ownerPackageName, ownerUid, sessionId, sessionFile,
+                mock(BlobHandle.class));
+    }
+    private BlobStoreSession createBlobStoreSessionMock(String ownerPackageName, int ownerUid,
+            long sessionId, File sessionFile, BlobHandle blobHandle) {
+        final BlobStoreSession session = mock(BlobStoreSession.class);
+        doReturn(ownerPackageName).when(session).getOwnerPackageName();
+        doReturn(ownerUid).when(session).getOwnerUid();
+        doReturn(sessionId).when(session).getSessionId();
+        doReturn(sessionFile).when(session).getSessionFile();
+        doReturn(blobHandle).when(session).getBlobHandle();
+        return session;
+    }
+
+    private BlobMetadata createBlobMetadataMock(long blobId, File blobFile, boolean hasLeases) {
+        final BlobMetadata blobMetadata = mock(BlobMetadata.class);
+        doReturn(blobId).when(blobMetadata).getBlobId();
+        doReturn(blobFile).when(blobMetadata).getBlobFile();
+        doReturn(hasLeases).when(blobMetadata).hasLeases();
+        return blobMetadata;
+    }
+
+    private class TestHandler extends Handler {
+        TestHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void dispatchMessage(Message msg) {
+            // Ignore all messages
+        }
+    }
+
+    private class TestInjector extends Injector {
+        @Override
+        public Handler initializeMessageHandler() {
+            return mHandler;
+        }
+    }
+}
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 8381205..f990810 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -46,7 +46,6 @@
         "service-appsearch",
         "service-jobscheduler",
         "service-permission",
-        "service-blobstore",
         // TODO: remove once Android migrates to JUnit 4.12,
         // which provides assertThrows
         "testng",
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index d2ddff3..1212f20 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -65,6 +65,8 @@
     <uses-permission android:name="android.permission.WATCH_APPOPS" />
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
     <uses-permission android:name="android.permission.SUSPEND_APPS"/>
+    <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE" />
+    <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
     <uses-permission android:name="android.permission.CONTROL_KEYGUARD"/>
     <uses-permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE"/>
     <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
diff --git a/services/tests/servicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
deleted file mode 100644
index ff728e7..0000000
--- a/services/tests/servicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright 2020 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.server.blob;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.blob.BlobHandle;
-import android.content.Context;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.UserHandle;
-import android.platform.test.annotations.Presubmit;
-import android.util.ArrayMap;
-import android.util.LongSparseArray;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.server.blob.BlobStoreManagerService.Injector;
-import com.android.server.blob.BlobStoreManagerService.SessionStateChangeListener;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.File;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class BlobStoreManagerServiceTest {
-    private Context mContext;
-    private Handler mHandler;
-    private BlobStoreManagerService mService;
-
-    private LongSparseArray<BlobStoreSession> mUserSessions;
-    private ArrayMap<BlobHandle, BlobMetadata> mUserBlobs;
-
-    private SessionStateChangeListener mStateChangeListener;
-
-    private static final String TEST_PKG1 = "com.example1";
-    private static final String TEST_PKG2 = "com.example2";
-    private static final String TEST_PKG3 = "com.example3";
-
-    private static final int TEST_UID1 = 10001;
-    private static final int TEST_UID2 = 10002;
-    private static final int TEST_UID3 = 10003;
-
-    @Before
-    public void setUp() {
-        // Share classloader to allow package private access.
-        System.setProperty("dexmaker.share_classloader", "true");
-
-        mContext = InstrumentationRegistry.getTargetContext();
-        mHandler = new TestHandler(Looper.getMainLooper());
-        mService = new BlobStoreManagerService(mContext, new TestInjector());
-        mUserSessions = new LongSparseArray<>();
-        mUserBlobs = new ArrayMap<>();
-
-        mService.addUserSessionsForTest(mUserSessions, UserHandle.myUserId());
-        mService.addUserBlobsForTest(mUserBlobs, UserHandle.myUserId());
-
-        mStateChangeListener = mService.new SessionStateChangeListener();
-    }
-
-    @Test
-    public void testHandlePackageRemoved() throws Exception {
-        // Setup sessions
-        final File sessionFile1 = mock(File.class);
-        final long sessionId1 = 11;
-        final BlobStoreSession session1 = createBlobStoreSessionMock(TEST_PKG1, TEST_UID1,
-                sessionId1, sessionFile1);
-        mUserSessions.append(sessionId1, session1);
-
-        final File sessionFile2 = mock(File.class);
-        final long sessionId2 = 25;
-        final BlobStoreSession session2 = createBlobStoreSessionMock(TEST_PKG2, TEST_UID2,
-                sessionId2, sessionFile2);
-        mUserSessions.append(sessionId2, session2);
-
-        final File sessionFile3 = mock(File.class);
-        final long sessionId3 = 37;
-        final BlobStoreSession session3 = createBlobStoreSessionMock(TEST_PKG3, TEST_UID3,
-                sessionId3, sessionFile3);
-        mUserSessions.append(sessionId3, session3);
-
-        final File sessionFile4 = mock(File.class);
-        final long sessionId4 = 48;
-        final BlobStoreSession session4 = createBlobStoreSessionMock(TEST_PKG1, TEST_UID1,
-                sessionId4, sessionFile4);
-        mUserSessions.append(sessionId4, session4);
-
-        // Setup blobs
-        final File blobFile1 = mock(File.class);
-        final BlobHandle blobHandle1 = BlobHandle.createWithSha256("digest1".getBytes(),
-                "label1", System.currentTimeMillis(), "tag1");
-        final BlobMetadata blobMetadata1 = createBlobMetadataMock(blobFile1, true);
-        mUserBlobs.put(blobHandle1, blobMetadata1);
-
-        final File blobFile2 = mock(File.class);
-        final BlobHandle blobHandle2 = BlobHandle.createWithSha256("digest2".getBytes(),
-                "label2", System.currentTimeMillis(), "tag2");
-        final BlobMetadata blobMetadata2 = createBlobMetadataMock(blobFile2, false);
-        mUserBlobs.put(blobHandle2, blobMetadata2);
-
-        // Invoke test method
-        mService.handlePackageRemoved(TEST_PKG1, TEST_UID1);
-
-        // Verify sessions are removed
-        verify(sessionFile1).delete();
-        verify(sessionFile2, never()).delete();
-        verify(sessionFile3, never()).delete();
-        verify(sessionFile4).delete();
-
-        assertThat(mUserSessions.size()).isEqualTo(2);
-        assertThat(mUserSessions.get(sessionId1)).isNull();
-        assertThat(mUserSessions.get(sessionId2)).isNotNull();
-        assertThat(mUserSessions.get(sessionId3)).isNotNull();
-        assertThat(mUserSessions.get(sessionId4)).isNull();
-
-        // Verify blobs are removed
-        verify(blobMetadata1).removeCommitter(TEST_PKG1, TEST_UID1);
-        verify(blobMetadata1).removeLeasee(TEST_PKG1, TEST_UID1);
-        verify(blobMetadata2).removeCommitter(TEST_PKG1, TEST_UID1);
-        verify(blobMetadata2).removeLeasee(TEST_PKG1, TEST_UID1);
-
-        verify(blobFile1, never()).delete();
-        verify(blobFile2).delete();
-
-        assertThat(mUserBlobs.size()).isEqualTo(1);
-        assertThat(mUserBlobs.get(blobHandle1)).isNotNull();
-        assertThat(mUserBlobs.get(blobHandle2)).isNull();
-    }
-
-    private BlobStoreSession createBlobStoreSessionMock(String ownerPackageName, int ownerUid,
-            long sessionId, File sessionFile) {
-        final BlobStoreSession session = mock(BlobStoreSession.class);
-        when(session.getOwnerPackageName()).thenReturn(ownerPackageName);
-        when(session.getOwnerUid()).thenReturn(ownerUid);
-        when(session.getSessionId()).thenReturn(sessionId);
-        when(session.getSessionFile()).thenReturn(sessionFile);
-        return session;
-    }
-
-    private BlobMetadata createBlobMetadataMock(File blobFile, boolean hasLeases) {
-        final BlobMetadata blobMetadata = mock(BlobMetadata.class);
-        when(blobMetadata.getBlobFile()).thenReturn(blobFile);
-        when(blobMetadata.hasLeases()).thenReturn(hasLeases);
-        return blobMetadata;
-    }
-
-    private class TestHandler extends Handler {
-        TestHandler(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void dispatchMessage(Message msg) {
-            // Ignore all messages
-        }
-    }
-
-    private class TestInjector extends Injector {
-        @Override
-        public Handler initializeMessageHandler() {
-            return mHandler;
-        }
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
index bfe0c15..d4edab4 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
@@ -24,6 +24,7 @@
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
 import android.content.pm.PackageInstaller;
+import android.platform.test.annotations.Presubmit;
 import android.util.AtomicFile;
 import android.util.Slog;
 import android.util.Xml;
@@ -57,6 +58,7 @@
 import java.util.List;
 
 @RunWith(AndroidJUnit4.class)
+@Presubmit
 public class PackageInstallerSessionTest {
     @Rule
     public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
index 218f43c..2eeeb3e 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -30,7 +30,7 @@
 
 import android.app.timedetector.ManualTimeSuggestion;
 import android.app.timedetector.NetworkTimeSuggestion;
-import android.app.timedetector.PhoneTimeSuggestion;
+import android.app.timedetector.TelephonyTimeSuggestion;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.HandlerThread;
@@ -80,35 +80,35 @@
     }
 
     @Test(expected = SecurityException.class)
-    public void testSuggestPhoneTime_withoutPermission() {
+    public void testSuggestTelephonyTime_withoutPermission() {
         doThrow(new SecurityException("Mock"))
                 .when(mMockContext).enforceCallingPermission(anyString(), any());
-        PhoneTimeSuggestion phoneTimeSuggestion = createPhoneTimeSuggestion();
+        TelephonyTimeSuggestion timeSuggestion = createTelephonyTimeSuggestion();
 
         try {
-            mTimeDetectorService.suggestPhoneTime(phoneTimeSuggestion);
+            mTimeDetectorService.suggestTelephonyTime(timeSuggestion);
             fail();
         } finally {
             verify(mMockContext).enforceCallingPermission(
-                    eq(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE),
+                    eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE),
                     anyString());
         }
     }
 
     @Test
-    public void testSuggestPhoneTime() throws Exception {
+    public void testSuggestTelephonyTime() throws Exception {
         doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
 
-        PhoneTimeSuggestion phoneTimeSuggestion = createPhoneTimeSuggestion();
-        mTimeDetectorService.suggestPhoneTime(phoneTimeSuggestion);
+        TelephonyTimeSuggestion timeSuggestion = createTelephonyTimeSuggestion();
+        mTimeDetectorService.suggestTelephonyTime(timeSuggestion);
         mTestHandler.assertTotalMessagesEnqueued(1);
 
         verify(mMockContext).enforceCallingPermission(
-                eq(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE),
+                eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE),
                 anyString());
 
         mTestHandler.waitForMessagesToBeProcessed();
-        mStubbedTimeDetectorStrategy.verifySuggestPhoneTimeCalled(phoneTimeSuggestion);
+        mStubbedTimeDetectorStrategy.verifySuggestTelephonyTimeCalled(timeSuggestion);
     }
 
     @Test(expected = SecurityException.class)
@@ -199,10 +199,10 @@
         mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionChangedCalled();
     }
 
-    private static PhoneTimeSuggestion createPhoneTimeSuggestion() {
+    private static TelephonyTimeSuggestion createTelephonyTimeSuggestion() {
         int slotIndex = 1234;
         TimestampedValue<Long> timeValue = new TimestampedValue<>(100L, 1_000_000L);
-        return new PhoneTimeSuggestion.Builder(slotIndex)
+        return new TelephonyTimeSuggestion.Builder(slotIndex)
                 .setUtcTime(timeValue)
                 .build();
     }
@@ -220,7 +220,7 @@
     private static class StubbedTimeDetectorStrategy implements TimeDetectorStrategy {
 
         // Call tracking.
-        private PhoneTimeSuggestion mLastPhoneSuggestion;
+        private TelephonyTimeSuggestion mLastTelephonySuggestion;
         private ManualTimeSuggestion mLastManualSuggestion;
         private NetworkTimeSuggestion mLastNetworkSuggestion;
         private boolean mHandleAutoTimeDetectionChangedCalled;
@@ -231,8 +231,8 @@
         }
 
         @Override
-        public void suggestPhoneTime(PhoneTimeSuggestion timeSuggestion) {
-            mLastPhoneSuggestion = timeSuggestion;
+        public void suggestTelephonyTime(TelephonyTimeSuggestion timeSuggestion) {
+            mLastTelephonySuggestion = timeSuggestion;
         }
 
         @Override
@@ -256,15 +256,15 @@
         }
 
         void resetCallTracking() {
-            mLastPhoneSuggestion = null;
+            mLastTelephonySuggestion = null;
             mLastManualSuggestion = null;
             mLastNetworkSuggestion = null;
             mHandleAutoTimeDetectionChangedCalled = false;
             mDumpCalled = false;
         }
 
-        void verifySuggestPhoneTimeCalled(PhoneTimeSuggestion expectedSuggestion) {
-            assertEquals(expectedSuggestion, mLastPhoneSuggestion);
+        void verifySuggestTelephonyTimeCalled(TelephonyTimeSuggestion expectedSuggestion) {
+            assertEquals(expectedSuggestion, mLastTelephonySuggestion);
         }
 
         public void verifySuggestManualTimeCalled(ManualTimeSuggestion expectedSuggestion) {
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
index d940a6a..803b245 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
@@ -24,7 +24,7 @@
 
 import android.app.timedetector.ManualTimeSuggestion;
 import android.app.timedetector.NetworkTimeSuggestion;
-import android.app.timedetector.PhoneTimeSuggestion;
+import android.app.timedetector.TelephonyTimeSuggestion;
 import android.icu.util.Calendar;
 import android.icu.util.GregorianCalendar;
 import android.icu.util.TimeZone;
@@ -52,7 +52,7 @@
      */
     private static final long ARBITRARY_TEST_TIME_MILLIS = createUtcTime(2018, 1, 1, 12, 0, 0);
 
-    private static final int ARBITRARY_PHONE_ID = 123456;
+    private static final int ARBITRARY_SLOT_INDEX = 123456;
 
     private Script mScript;
 
@@ -62,51 +62,51 @@
     }
 
     @Test
-    public void testSuggestPhoneTime_autoTimeEnabled() {
+    public void testSuggestTelephonyTime_autoTimeEnabled() {
         mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
                 .pokeAutoTimeDetectionEnabled(true);
 
-        int phoneId = ARBITRARY_PHONE_ID;
+        int slotIndex = ARBITRARY_SLOT_INDEX;
         long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
 
-        PhoneTimeSuggestion timeSuggestion =
-                mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
+        TelephonyTimeSuggestion timeSuggestion =
+                mScript.generateTelephonyTimeSuggestion(slotIndex, testTimeMillis);
         mScript.simulateTimePassing()
-                .simulatePhoneTimeSuggestion(timeSuggestion);
+                .simulateTelephonyTimeSuggestion(timeSuggestion);
 
         long expectedSystemClockMillis =
                 mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime());
         mScript.verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis)
-                .assertLatestPhoneSuggestion(phoneId, timeSuggestion);
+                .assertLatestTelephonySuggestion(slotIndex, timeSuggestion);
     }
 
     @Test
-    public void testSuggestPhoneTime_emptySuggestionIgnored() {
+    public void testSuggestTelephonyTime_emptySuggestionIgnored() {
         mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
                 .pokeAutoTimeDetectionEnabled(true);
 
-        int phoneId = ARBITRARY_PHONE_ID;
-        PhoneTimeSuggestion timeSuggestion =
-                mScript.generatePhoneTimeSuggestion(phoneId, null);
-        mScript.simulatePhoneTimeSuggestion(timeSuggestion)
+        int slotIndex = ARBITRARY_SLOT_INDEX;
+        TelephonyTimeSuggestion timeSuggestion =
+                mScript.generateTelephonyTimeSuggestion(slotIndex, null);
+        mScript.simulateTelephonyTimeSuggestion(timeSuggestion)
                 .verifySystemClockWasNotSetAndResetCallTracking()
-                .assertLatestPhoneSuggestion(phoneId, null);
+                .assertLatestTelephonySuggestion(slotIndex, null);
     }
 
     @Test
-    public void testSuggestPhoneTime_systemClockThreshold() {
+    public void testSuggestTelephonyTime_systemClockThreshold() {
         final int systemClockUpdateThresholdMillis = 1000;
         final int clockIncrementMillis = 100;
         mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
                 .pokeThresholds(systemClockUpdateThresholdMillis)
                 .pokeAutoTimeDetectionEnabled(true);
 
-        int phoneId = ARBITRARY_PHONE_ID;
+        int slotIndex = ARBITRARY_SLOT_INDEX;
 
         // Send the first time signal. It should be used.
         {
-            PhoneTimeSuggestion timeSuggestion1 =
-                    mScript.generatePhoneTimeSuggestion(phoneId, ARBITRARY_TEST_TIME_MILLIS);
+            TelephonyTimeSuggestion timeSuggestion1 =
+                    mScript.generateTelephonyTimeSuggestion(slotIndex, ARBITRARY_TEST_TIME_MILLIS);
 
             // Increment the the device clocks to simulate the passage of time.
             mScript.simulateTimePassing(clockIncrementMillis);
@@ -114,151 +114,151 @@
             long expectedSystemClockMillis1 =
                     mScript.calculateTimeInMillisForNow(timeSuggestion1.getUtcTime());
 
-            mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
+            mScript.simulateTelephonyTimeSuggestion(timeSuggestion1)
                     .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1)
-                    .assertLatestPhoneSuggestion(phoneId, timeSuggestion1);
+                    .assertLatestTelephonySuggestion(slotIndex, timeSuggestion1);
         }
 
         // Now send another time signal, but one that is too similar to the last one and should be
         // stored, but not used to set the system clock.
         {
             int underThresholdMillis = systemClockUpdateThresholdMillis - 1;
-            PhoneTimeSuggestion timeSuggestion2 = mScript.generatePhoneTimeSuggestion(
-                    phoneId, mScript.peekSystemClockMillis() + underThresholdMillis);
+            TelephonyTimeSuggestion timeSuggestion2 = mScript.generateTelephonyTimeSuggestion(
+                    slotIndex, mScript.peekSystemClockMillis() + underThresholdMillis);
             mScript.simulateTimePassing(clockIncrementMillis)
-                    .simulatePhoneTimeSuggestion(timeSuggestion2)
+                    .simulateTelephonyTimeSuggestion(timeSuggestion2)
                     .verifySystemClockWasNotSetAndResetCallTracking()
-                    .assertLatestPhoneSuggestion(phoneId, timeSuggestion2);
+                    .assertLatestTelephonySuggestion(slotIndex, timeSuggestion2);
         }
 
         // Now send another time signal, but one that is on the threshold and so should be used.
         {
-            PhoneTimeSuggestion timeSuggestion3 = mScript.generatePhoneTimeSuggestion(
-                    phoneId,
+            TelephonyTimeSuggestion timeSuggestion3 = mScript.generateTelephonyTimeSuggestion(
+                    slotIndex,
                     mScript.peekSystemClockMillis() + systemClockUpdateThresholdMillis);
             mScript.simulateTimePassing(clockIncrementMillis);
 
             long expectedSystemClockMillis3 =
                     mScript.calculateTimeInMillisForNow(timeSuggestion3.getUtcTime());
 
-            mScript.simulatePhoneTimeSuggestion(timeSuggestion3)
+            mScript.simulateTelephonyTimeSuggestion(timeSuggestion3)
                     .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis3)
-                    .assertLatestPhoneSuggestion(phoneId, timeSuggestion3);
+                    .assertLatestTelephonySuggestion(slotIndex, timeSuggestion3);
         }
     }
 
     @Test
-    public void testSuggestPhoneTime_multiplePhoneIdsAndBucketing() {
+    public void testSuggestTelephonyTime_multipleSlotIndexsAndBucketing() {
         mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
                 .pokeAutoTimeDetectionEnabled(true);
 
-        // There are 2 phones in this test. Phone 2 has a different idea of the current time.
-        // phone1Id < phone2Id (which is important because the strategy uses the lowest ID when
-        // multiple phone suggestions are available.
-        int phone1Id = ARBITRARY_PHONE_ID;
-        int phone2Id = ARBITRARY_PHONE_ID + 1;
-        long phone1TimeMillis = ARBITRARY_TEST_TIME_MILLIS;
-        long phone2TimeMillis = ARBITRARY_TEST_TIME_MILLIS + Duration.ofDays(1).toMillis();
+        // There are 2 slotIndexes in this test. slotIndex1 and slotIndex2 have different opinions
+        // about the current time. slotIndex1 < slotIndex2 (which is important because the strategy
+        // uses the lowest slotIndex when multiple telephony suggestions are available.
+        int slotIndex1 = ARBITRARY_SLOT_INDEX;
+        int slotIndex2 = ARBITRARY_SLOT_INDEX + 1;
+        long slotIndex1TimeMillis = ARBITRARY_TEST_TIME_MILLIS;
+        long slotIndex2TimeMillis = ARBITRARY_TEST_TIME_MILLIS + Duration.ofDays(1).toMillis();
 
-        // Make a suggestion with phone2Id.
+        // Make a suggestion with slotIndex2.
         {
-            PhoneTimeSuggestion phone2TimeSuggestion =
-                    mScript.generatePhoneTimeSuggestion(phone2Id, phone2TimeMillis);
+            TelephonyTimeSuggestion slotIndex2TimeSuggestion =
+                    mScript.generateTelephonyTimeSuggestion(slotIndex2, slotIndex2TimeMillis);
             mScript.simulateTimePassing();
 
             long expectedSystemClockMillis =
-                    mScript.calculateTimeInMillisForNow(phone2TimeSuggestion.getUtcTime());
+                    mScript.calculateTimeInMillisForNow(slotIndex2TimeSuggestion.getUtcTime());
 
-            mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion)
+            mScript.simulateTelephonyTimeSuggestion(slotIndex2TimeSuggestion)
                     .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis)
-                    .assertLatestPhoneSuggestion(phone1Id, null)
-                    .assertLatestPhoneSuggestion(phone2Id, phone2TimeSuggestion);
+                    .assertLatestTelephonySuggestion(slotIndex1, null)
+                    .assertLatestTelephonySuggestion(slotIndex2, slotIndex2TimeSuggestion);
         }
 
         mScript.simulateTimePassing();
 
-        // Now make a different suggestion with phone1Id.
+        // Now make a different suggestion with slotIndex1.
         {
-            PhoneTimeSuggestion phone1TimeSuggestion =
-                    mScript.generatePhoneTimeSuggestion(phone1Id, phone1TimeMillis);
+            TelephonyTimeSuggestion slotIndex1TimeSuggestion =
+                    mScript.generateTelephonyTimeSuggestion(slotIndex1, slotIndex1TimeMillis);
             mScript.simulateTimePassing();
 
             long expectedSystemClockMillis =
-                    mScript.calculateTimeInMillisForNow(phone1TimeSuggestion.getUtcTime());
+                    mScript.calculateTimeInMillisForNow(slotIndex1TimeSuggestion.getUtcTime());
 
-            mScript.simulatePhoneTimeSuggestion(phone1TimeSuggestion)
+            mScript.simulateTelephonyTimeSuggestion(slotIndex1TimeSuggestion)
                     .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis)
-                    .assertLatestPhoneSuggestion(phone1Id, phone1TimeSuggestion);
+                    .assertLatestTelephonySuggestion(slotIndex1, slotIndex1TimeSuggestion);
 
         }
 
         mScript.simulateTimePassing();
 
-        // Make another suggestion with phone2Id. It should be stored but not used because the
-        // phone1Id suggestion will still "win".
+        // Make another suggestion with slotIndex2. It should be stored but not used because the
+        // slotIndex1 suggestion will still "win".
         {
-            PhoneTimeSuggestion phone2TimeSuggestion =
-                    mScript.generatePhoneTimeSuggestion(phone2Id, phone2TimeMillis);
+            TelephonyTimeSuggestion slotIndex2TimeSuggestion =
+                    mScript.generateTelephonyTimeSuggestion(slotIndex2, slotIndex2TimeMillis);
             mScript.simulateTimePassing();
 
-            mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion)
+            mScript.simulateTelephonyTimeSuggestion(slotIndex2TimeSuggestion)
                     .verifySystemClockWasNotSetAndResetCallTracking()
-                    .assertLatestPhoneSuggestion(phone2Id, phone2TimeSuggestion);
+                    .assertLatestTelephonySuggestion(slotIndex2, slotIndex2TimeSuggestion);
         }
 
-        // Let enough time pass that phone1Id's suggestion should now be too old.
-        mScript.simulateTimePassing(TimeDetectorStrategyImpl.PHONE_BUCKET_SIZE_MILLIS);
+        // Let enough time pass that slotIndex1's suggestion should now be too old.
+        mScript.simulateTimePassing(TimeDetectorStrategyImpl.TELEPHONY_BUCKET_SIZE_MILLIS);
 
-        // Make another suggestion with phone2Id. It should be used because the phoneId1
+        // Make another suggestion with slotIndex2. It should be used because the slotIndex1
         // is in an older "bucket".
         {
-            PhoneTimeSuggestion phone2TimeSuggestion =
-                    mScript.generatePhoneTimeSuggestion(phone2Id, phone2TimeMillis);
+            TelephonyTimeSuggestion slotIndex2TimeSuggestion =
+                    mScript.generateTelephonyTimeSuggestion(slotIndex2, slotIndex2TimeMillis);
             mScript.simulateTimePassing();
 
             long expectedSystemClockMillis =
-                    mScript.calculateTimeInMillisForNow(phone2TimeSuggestion.getUtcTime());
+                    mScript.calculateTimeInMillisForNow(slotIndex2TimeSuggestion.getUtcTime());
 
-            mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion)
+            mScript.simulateTelephonyTimeSuggestion(slotIndex2TimeSuggestion)
                     .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis)
-                    .assertLatestPhoneSuggestion(phone2Id, phone2TimeSuggestion);
+                    .assertLatestTelephonySuggestion(slotIndex2, slotIndex2TimeSuggestion);
         }
     }
 
     @Test
-    public void testSuggestPhoneTime_autoTimeDisabled() {
+    public void testSuggestTelephonyTime_autoTimeDisabled() {
         mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
                 .pokeAutoTimeDetectionEnabled(false);
 
-        int phoneId = ARBITRARY_PHONE_ID;
-        PhoneTimeSuggestion timeSuggestion =
-                mScript.generatePhoneTimeSuggestion(phoneId, ARBITRARY_TEST_TIME_MILLIS);
+        int slotIndex = ARBITRARY_SLOT_INDEX;
+        TelephonyTimeSuggestion timeSuggestion =
+                mScript.generateTelephonyTimeSuggestion(slotIndex, ARBITRARY_TEST_TIME_MILLIS);
         mScript.simulateTimePassing()
-                .simulatePhoneTimeSuggestion(timeSuggestion)
+                .simulateTelephonyTimeSuggestion(timeSuggestion)
                 .verifySystemClockWasNotSetAndResetCallTracking()
-                .assertLatestPhoneSuggestion(phoneId, timeSuggestion);
+                .assertLatestTelephonySuggestion(slotIndex, timeSuggestion);
     }
 
     @Test
-    public void testSuggestPhoneTime_invalidNitzReferenceTimesIgnored() {
+    public void testSuggestTelephonyTime_invalidNitzReferenceTimesIgnored() {
         final int systemClockUpdateThreshold = 2000;
         mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
                 .pokeThresholds(systemClockUpdateThreshold)
                 .pokeAutoTimeDetectionEnabled(true);
 
         long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
-        int phoneId = ARBITRARY_PHONE_ID;
+        int slotIndex = ARBITRARY_SLOT_INDEX;
 
-        PhoneTimeSuggestion timeSuggestion1 =
-                mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
+        TelephonyTimeSuggestion timeSuggestion1 =
+                mScript.generateTelephonyTimeSuggestion(slotIndex, testTimeMillis);
         TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
 
-        // Initialize the strategy / device with a time set from a phone suggestion.
+        // Initialize the strategy / device with a time set from a telephony suggestion.
         mScript.simulateTimePassing();
         long expectedSystemClockMillis1 = mScript.calculateTimeInMillisForNow(utcTime1);
-        mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
+        mScript.simulateTelephonyTimeSuggestion(timeSuggestion1)
                 .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1)
-                .assertLatestPhoneSuggestion(phoneId, timeSuggestion1);
+                .assertLatestTelephonySuggestion(slotIndex, timeSuggestion1);
 
         // The UTC time increment should be larger than the system clock update threshold so we
         // know it shouldn't be ignored for other reasons.
@@ -269,11 +269,11 @@
         long referenceTimeBeforeLastSignalMillis = utcTime1.getReferenceTimeMillis() - 1;
         TimestampedValue<Long> utcTime2 = new TimestampedValue<>(
                 referenceTimeBeforeLastSignalMillis, validUtcTimeMillis);
-        PhoneTimeSuggestion timeSuggestion2 =
-                createPhoneTimeSuggestion(phoneId, utcTime2);
-        mScript.simulatePhoneTimeSuggestion(timeSuggestion2)
+        TelephonyTimeSuggestion timeSuggestion2 =
+                createTelephonyTimeSuggestion(slotIndex, utcTime2);
+        mScript.simulateTelephonyTimeSuggestion(timeSuggestion2)
                 .verifySystemClockWasNotSetAndResetCallTracking()
-                .assertLatestPhoneSuggestion(phoneId, timeSuggestion1);
+                .assertLatestTelephonySuggestion(slotIndex, timeSuggestion1);
 
         // Now supply a new signal that has an obviously bogus reference time : substantially in the
         // future.
@@ -281,36 +281,36 @@
                 utcTime1.getReferenceTimeMillis() + Integer.MAX_VALUE + 1;
         TimestampedValue<Long> utcTime3 = new TimestampedValue<>(
                 referenceTimeInFutureMillis, validUtcTimeMillis);
-        PhoneTimeSuggestion timeSuggestion3 =
-                createPhoneTimeSuggestion(phoneId, utcTime3);
-        mScript.simulatePhoneTimeSuggestion(timeSuggestion3)
+        TelephonyTimeSuggestion timeSuggestion3 =
+                createTelephonyTimeSuggestion(slotIndex, utcTime3);
+        mScript.simulateTelephonyTimeSuggestion(timeSuggestion3)
                 .verifySystemClockWasNotSetAndResetCallTracking()
-                .assertLatestPhoneSuggestion(phoneId, timeSuggestion1);
+                .assertLatestTelephonySuggestion(slotIndex, timeSuggestion1);
 
         // Just to prove validUtcTimeMillis is valid.
         long validReferenceTimeMillis = utcTime1.getReferenceTimeMillis() + 100;
         TimestampedValue<Long> utcTime4 = new TimestampedValue<>(
                 validReferenceTimeMillis, validUtcTimeMillis);
         long expectedSystemClockMillis4 = mScript.calculateTimeInMillisForNow(utcTime4);
-        PhoneTimeSuggestion timeSuggestion4 =
-                createPhoneTimeSuggestion(phoneId, utcTime4);
-        mScript.simulatePhoneTimeSuggestion(timeSuggestion4)
+        TelephonyTimeSuggestion timeSuggestion4 =
+                createTelephonyTimeSuggestion(slotIndex, utcTime4);
+        mScript.simulateTelephonyTimeSuggestion(timeSuggestion4)
                 .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis4)
-                .assertLatestPhoneSuggestion(phoneId, timeSuggestion4);
+                .assertLatestTelephonySuggestion(slotIndex, timeSuggestion4);
     }
 
     @Test
-    public void testSuggestPhoneTime_timeDetectionToggled() {
+    public void testSuggestTelephonyTime_timeDetectionToggled() {
         final int clockIncrementMillis = 100;
         final int systemClockUpdateThreshold = 2000;
         mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
                 .pokeThresholds(systemClockUpdateThreshold)
                 .pokeAutoTimeDetectionEnabled(false);
 
-        int phoneId = ARBITRARY_PHONE_ID;
+        int slotIndex = ARBITRARY_SLOT_INDEX;
         long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
-        PhoneTimeSuggestion timeSuggestion1 =
-                mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
+        TelephonyTimeSuggestion timeSuggestion1 =
+                mScript.generateTelephonyTimeSuggestion(slotIndex, testTimeMillis);
         TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
 
         // Simulate time passing.
@@ -318,9 +318,9 @@
 
         // Simulate the time signal being received. It should not be used because auto time
         // detection is off but it should be recorded.
-        mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
+        mScript.simulateTelephonyTimeSuggestion(timeSuggestion1)
                 .verifySystemClockWasNotSetAndResetCallTracking()
-                .assertLatestPhoneSuggestion(phoneId, timeSuggestion1);
+                .assertLatestTelephonySuggestion(slotIndex, timeSuggestion1);
 
         // Simulate more time passing.
         mScript.simulateTimePassing(clockIncrementMillis);
@@ -330,17 +330,17 @@
         // Turn on auto time detection.
         mScript.simulateAutoTimeDetectionToggle()
                 .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1)
-                .assertLatestPhoneSuggestion(phoneId, timeSuggestion1);
+                .assertLatestTelephonySuggestion(slotIndex, timeSuggestion1);
 
         // Turn off auto time detection.
         mScript.simulateAutoTimeDetectionToggle()
                 .verifySystemClockWasNotSetAndResetCallTracking()
-                .assertLatestPhoneSuggestion(phoneId, timeSuggestion1);
+                .assertLatestTelephonySuggestion(slotIndex, timeSuggestion1);
 
         // Receive another valid time signal.
         // It should be on the threshold and accounting for the clock increments.
-        PhoneTimeSuggestion timeSuggestion2 = mScript.generatePhoneTimeSuggestion(
-                phoneId, mScript.peekSystemClockMillis() + systemClockUpdateThreshold);
+        TelephonyTimeSuggestion timeSuggestion2 = mScript.generateTelephonyTimeSuggestion(
+                slotIndex, mScript.peekSystemClockMillis() + systemClockUpdateThreshold);
 
         // Simulate more time passing.
         mScript.simulateTimePassing(clockIncrementMillis);
@@ -350,45 +350,45 @@
 
         // The new time, though valid, should not be set in the system clock because auto time is
         // disabled.
-        mScript.simulatePhoneTimeSuggestion(timeSuggestion2)
+        mScript.simulateTelephonyTimeSuggestion(timeSuggestion2)
                 .verifySystemClockWasNotSetAndResetCallTracking()
-                .assertLatestPhoneSuggestion(phoneId, timeSuggestion2);
+                .assertLatestTelephonySuggestion(slotIndex, timeSuggestion2);
 
         // Turn on auto time detection.
         mScript.simulateAutoTimeDetectionToggle()
                 .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis2)
-                .assertLatestPhoneSuggestion(phoneId, timeSuggestion2);
+                .assertLatestTelephonySuggestion(slotIndex, timeSuggestion2);
     }
 
     @Test
-    public void testSuggestPhoneTime_maxSuggestionAge() {
+    public void testSuggestTelephonyTime_maxSuggestionAge() {
         mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
                 .pokeAutoTimeDetectionEnabled(true);
 
-        int phoneId = ARBITRARY_PHONE_ID;
+        int slotIndex = ARBITRARY_SLOT_INDEX;
         long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
-        PhoneTimeSuggestion phoneSuggestion =
-                mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
+        TelephonyTimeSuggestion telephonySuggestion =
+                mScript.generateTelephonyTimeSuggestion(slotIndex, testTimeMillis);
 
         mScript.simulateTimePassing();
 
         long expectedSystemClockMillis =
-                mScript.calculateTimeInMillisForNow(phoneSuggestion.getUtcTime());
-        mScript.simulatePhoneTimeSuggestion(phoneSuggestion)
+                mScript.calculateTimeInMillisForNow(telephonySuggestion.getUtcTime());
+        mScript.simulateTelephonyTimeSuggestion(telephonySuggestion)
                 .verifySystemClockWasSetAndResetCallTracking(
                         expectedSystemClockMillis  /* expectedNetworkBroadcast */)
-                .assertLatestPhoneSuggestion(phoneId, phoneSuggestion);
+                .assertLatestTelephonySuggestion(slotIndex, telephonySuggestion);
 
-        // Look inside and check what the strategy considers the current best phone suggestion.
-        assertEquals(phoneSuggestion, mScript.peekBestPhoneSuggestion());
+        // Look inside and check what the strategy considers the current best telephony suggestion.
+        assertEquals(telephonySuggestion, mScript.peekBestTelephonySuggestion());
 
-        // Simulate time passing, long enough that phoneSuggestion is now too old.
+        // Simulate time passing, long enough that telephonySuggestion is now too old.
         mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_UTC_TIME_AGE_MILLIS);
 
-        // Look inside and check what the strategy considers the current best phone suggestion. It
-        // should still be the, it's just no longer used.
-        assertNull(mScript.peekBestPhoneSuggestion());
-        mScript.assertLatestPhoneSuggestion(phoneId, phoneSuggestion);
+        // Look inside and check what the strategy considers the current best telephony suggestion.
+        // It should still be the, it's just no longer used.
+        assertNull(mScript.peekBestTelephonySuggestion());
+        mScript.assertLatestTelephonySuggestion(slotIndex, telephonySuggestion);
     }
 
     @Test
@@ -413,21 +413,21 @@
         mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
                 .pokeAutoTimeDetectionEnabled(true);
 
-        int phoneId = ARBITRARY_PHONE_ID;
+        int slotIndex = ARBITRARY_SLOT_INDEX;
 
-        // Simulate a phone suggestion.
+        // Simulate a telephony suggestion.
         long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
-        PhoneTimeSuggestion phoneTimeSuggestion =
-                mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
+        TelephonyTimeSuggestion telephonyTimeSuggestion =
+                mScript.generateTelephonyTimeSuggestion(slotIndex, testTimeMillis);
 
         // Simulate the passage of time.
         mScript.simulateTimePassing();
 
         long expectedAutoClockMillis =
-                mScript.calculateTimeInMillisForNow(phoneTimeSuggestion.getUtcTime());
-        mScript.simulatePhoneTimeSuggestion(phoneTimeSuggestion)
+                mScript.calculateTimeInMillisForNow(telephonyTimeSuggestion.getUtcTime());
+        mScript.simulateTelephonyTimeSuggestion(telephonyTimeSuggestion)
                 .verifySystemClockWasSetAndResetCallTracking(expectedAutoClockMillis)
-                .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
+                .assertLatestTelephonySuggestion(slotIndex, telephonyTimeSuggestion);
 
         // Simulate the passage of time.
         mScript.simulateTimePassing();
@@ -435,7 +435,7 @@
         // Switch to manual.
         mScript.simulateAutoTimeDetectionToggle()
                 .verifySystemClockWasNotSetAndResetCallTracking()
-                .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
+                .assertLatestTelephonySuggestion(slotIndex, telephonyTimeSuggestion);
 
         // Simulate the passage of time.
         mScript.simulateTimePassing();
@@ -450,7 +450,7 @@
                 mScript.calculateTimeInMillisForNow(manualTimeSuggestion.getUtcTime());
         mScript.simulateManualTimeSuggestion(manualTimeSuggestion)
                 .verifySystemClockWasSetAndResetCallTracking(expectedManualClockMillis)
-                .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
+                .assertLatestTelephonySuggestion(slotIndex, telephonyTimeSuggestion);
 
         // Simulate the passage of time.
         mScript.simulateTimePassing();
@@ -459,14 +459,14 @@
         mScript.simulateAutoTimeDetectionToggle();
 
         expectedAutoClockMillis =
-                mScript.calculateTimeInMillisForNow(phoneTimeSuggestion.getUtcTime());
+                mScript.calculateTimeInMillisForNow(telephonyTimeSuggestion.getUtcTime());
         mScript.verifySystemClockWasSetAndResetCallTracking(expectedAutoClockMillis)
-                .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
+                .assertLatestTelephonySuggestion(slotIndex, telephonyTimeSuggestion);
 
         // Switch back to manual - nothing should happen to the clock.
         mScript.simulateAutoTimeDetectionToggle()
                 .verifySystemClockWasNotSetAndResetCallTracking()
-                .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
+                .assertLatestTelephonySuggestion(slotIndex, telephonyTimeSuggestion);
     }
 
     /**
@@ -515,19 +515,19 @@
     }
 
     @Test
-    public void testSuggestNetworkTime_phoneSuggestionsBeatNetworkSuggestions() {
+    public void testSuggestNetworkTime_telephonySuggestionsBeatNetworkSuggestions() {
         mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
                 .pokeAutoTimeDetectionEnabled(true);
 
         // Three obviously different times that could not be mistaken for each other.
         long networkTimeMillis1 = ARBITRARY_TEST_TIME_MILLIS;
         long networkTimeMillis2 = ARBITRARY_TEST_TIME_MILLIS + Duration.ofDays(30).toMillis();
-        long phoneTimeMillis = ARBITRARY_TEST_TIME_MILLIS + Duration.ofDays(60).toMillis();
+        long telephonyTimeMillis = ARBITRARY_TEST_TIME_MILLIS + Duration.ofDays(60).toMillis();
         // A small increment used to simulate the passage of time, but not enough to interfere with
         // macro-level time changes associated with suggestion age.
         final long smallTimeIncrementMillis = 101;
 
-        // A network suggestion is made. It should be used because there is no phone suggestion.
+        // A network suggestion is made. It should be used because there is no telephony suggestion.
         NetworkTimeSuggestion networkTimeSuggestion1 =
                 mScript.generateNetworkTimeSuggestion(networkTimeMillis1);
         mScript.simulateTimePassing(smallTimeIncrementMillis)
@@ -536,37 +536,37 @@
                         mScript.calculateTimeInMillisForNow(networkTimeSuggestion1.getUtcTime()));
 
         // Check internal state.
-        mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, null)
+        mScript.assertLatestTelephonySuggestion(ARBITRARY_SLOT_INDEX, null)
                 .assertLatestNetworkSuggestion(networkTimeSuggestion1);
         assertEquals(networkTimeSuggestion1, mScript.peekLatestValidNetworkSuggestion());
-        assertNull(mScript.peekBestPhoneSuggestion());
+        assertNull(mScript.peekBestTelephonySuggestion());
 
         // Simulate a little time passing.
         mScript.simulateTimePassing(smallTimeIncrementMillis)
                 .verifySystemClockWasNotSetAndResetCallTracking();
 
-        // Now a phone suggestion is made. Phone suggestions are prioritized over network
+        // Now a telephony suggestion is made. Telephony suggestions are prioritized over network
         // suggestions so it should "win".
-        PhoneTimeSuggestion phoneTimeSuggestion =
-                mScript.generatePhoneTimeSuggestion(ARBITRARY_PHONE_ID, phoneTimeMillis);
+        TelephonyTimeSuggestion telephonyTimeSuggestion =
+                mScript.generateTelephonyTimeSuggestion(ARBITRARY_SLOT_INDEX, telephonyTimeMillis);
         mScript.simulateTimePassing(smallTimeIncrementMillis)
-                .simulatePhoneTimeSuggestion(phoneTimeSuggestion)
+                .simulateTelephonyTimeSuggestion(telephonyTimeSuggestion)
                 .verifySystemClockWasSetAndResetCallTracking(
-                        mScript.calculateTimeInMillisForNow(phoneTimeSuggestion.getUtcTime()));
+                        mScript.calculateTimeInMillisForNow(telephonyTimeSuggestion.getUtcTime()));
 
         // Check internal state.
-        mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, phoneTimeSuggestion)
+        mScript.assertLatestTelephonySuggestion(ARBITRARY_SLOT_INDEX, telephonyTimeSuggestion)
                 .assertLatestNetworkSuggestion(networkTimeSuggestion1);
         assertEquals(networkTimeSuggestion1, mScript.peekLatestValidNetworkSuggestion());
-        assertEquals(phoneTimeSuggestion, mScript.peekBestPhoneSuggestion());
+        assertEquals(telephonyTimeSuggestion, mScript.peekBestTelephonySuggestion());
 
         // Simulate some significant time passing: half the time allowed before a time signal
         // becomes "too old to use".
         mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_UTC_TIME_AGE_MILLIS / 2)
                 .verifySystemClockWasNotSetAndResetCallTracking();
 
-        // Now another network suggestion is made. Phone suggestions are prioritized over network
-        // suggestions so the latest phone suggestion should still "win".
+        // Now another network suggestion is made. Telephony suggestions are prioritized over
+        // network suggestions so the latest telephony suggestion should still "win".
         NetworkTimeSuggestion networkTimeSuggestion2 =
                 mScript.generateNetworkTimeSuggestion(networkTimeMillis2);
         mScript.simulateTimePassing(smallTimeIncrementMillis)
@@ -574,14 +574,14 @@
                 .verifySystemClockWasNotSetAndResetCallTracking();
 
         // Check internal state.
-        mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, phoneTimeSuggestion)
+        mScript.assertLatestTelephonySuggestion(ARBITRARY_SLOT_INDEX, telephonyTimeSuggestion)
                 .assertLatestNetworkSuggestion(networkTimeSuggestion2);
         assertEquals(networkTimeSuggestion2, mScript.peekLatestValidNetworkSuggestion());
-        assertEquals(phoneTimeSuggestion, mScript.peekBestPhoneSuggestion());
+        assertEquals(telephonyTimeSuggestion, mScript.peekBestTelephonySuggestion());
 
         // Simulate some significant time passing: half the time allowed before a time signal
-        // becomes "too old to use". This should mean that phoneTimeSuggestion is now too old to be
-        // used but networkTimeSuggestion2 is not.
+        // becomes "too old to use". This should mean that telephonyTimeSuggestion is now too old to
+        // be used but networkTimeSuggestion2 is not.
         mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_UTC_TIME_AGE_MILLIS / 2);
 
         // NOTE: The TimeDetectorStrategyImpl doesn't set an alarm for the point when the last
@@ -591,10 +591,10 @@
         mScript.verifySystemClockWasNotSetAndResetCallTracking();
 
         // Check internal state.
-        mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, phoneTimeSuggestion)
+        mScript.assertLatestTelephonySuggestion(ARBITRARY_SLOT_INDEX, telephonyTimeSuggestion)
                 .assertLatestNetworkSuggestion(networkTimeSuggestion2);
         assertEquals(networkTimeSuggestion2, mScript.peekLatestValidNetworkSuggestion());
-        assertNull(mScript.peekBestPhoneSuggestion());
+        assertNull(mScript.peekBestTelephonySuggestion());
 
         // Toggle auto-time off and on to force the detection logic to run.
         mScript.simulateAutoTimeDetectionToggle()
@@ -606,10 +606,10 @@
                 mScript.calculateTimeInMillisForNow(networkTimeSuggestion2.getUtcTime()));
 
         // Check internal state.
-        mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, phoneTimeSuggestion)
+        mScript.assertLatestTelephonySuggestion(ARBITRARY_SLOT_INDEX, telephonyTimeSuggestion)
                 .assertLatestNetworkSuggestion(networkTimeSuggestion2);
         assertEquals(networkTimeSuggestion2, mScript.peekLatestValidNetworkSuggestion());
-        assertNull(mScript.peekBestPhoneSuggestion());
+        assertNull(mScript.peekBestTelephonySuggestion());
     }
 
     /**
@@ -760,8 +760,8 @@
             return mFakeCallback.peekSystemClockMillis();
         }
 
-        Script simulatePhoneTimeSuggestion(PhoneTimeSuggestion timeSuggestion) {
-            mTimeDetectorStrategy.suggestPhoneTime(timeSuggestion);
+        Script simulateTelephonyTimeSuggestion(TelephonyTimeSuggestion timeSuggestion) {
+            mTimeDetectorStrategy.suggestTelephonyTime(timeSuggestion);
             return this;
         }
 
@@ -806,10 +806,10 @@
         }
 
         /**
-         * White box test info: Asserts the latest suggestion for the phone ID is as expected.
+         * White box test info: Asserts the latest suggestion for the slotIndex is as expected.
          */
-        Script assertLatestPhoneSuggestion(int phoneId, PhoneTimeSuggestion expected) {
-            assertEquals(expected, mTimeDetectorStrategy.getLatestPhoneSuggestion(phoneId));
+        Script assertLatestTelephonySuggestion(int slotIndex, TelephonyTimeSuggestion expected) {
+            assertEquals(expected, mTimeDetectorStrategy.getLatestTelephonySuggestion(slotIndex));
             return this;
         }
 
@@ -822,11 +822,11 @@
         }
 
         /**
-         * White box test info: Returns the phone suggestion that would be used, if any, given the
-         * current elapsed real time clock and regardless of origin prioritization.
+         * White box test info: Returns the telephony suggestion that would be used, if any, given
+         * the current elapsed real time clock and regardless of origin prioritization.
          */
-        PhoneTimeSuggestion peekBestPhoneSuggestion() {
-            return mTimeDetectorStrategy.findBestPhoneSuggestionForTests();
+        TelephonyTimeSuggestion peekBestTelephonySuggestion() {
+            return mTimeDetectorStrategy.findBestTelephonySuggestionForTests();
         }
 
         /**
@@ -848,15 +848,15 @@
         }
 
         /**
-         * Generates a PhoneTimeSuggestion using the current elapsed realtime clock for the
-         * reference time.
+         * Generates a {@link TelephonyTimeSuggestion} using the current elapsed realtime clock for
+         * the reference time.
          */
-        PhoneTimeSuggestion generatePhoneTimeSuggestion(int phoneId, Long timeMillis) {
+        TelephonyTimeSuggestion generateTelephonyTimeSuggestion(int slotIndex, Long timeMillis) {
             TimestampedValue<Long> time = null;
             if (timeMillis != null) {
                 time = new TimestampedValue<>(peekElapsedRealtimeMillis(), timeMillis);
             }
-            return createPhoneTimeSuggestion(phoneId, time);
+            return createTelephonyTimeSuggestion(slotIndex, time);
         }
 
         /**
@@ -878,9 +878,9 @@
         }
     }
 
-    private static PhoneTimeSuggestion createPhoneTimeSuggestion(int phoneId,
+    private static TelephonyTimeSuggestion createTelephonyTimeSuggestion(int slotIndex,
             TimestampedValue<Long> utcTime) {
-        return new PhoneTimeSuggestion.Builder(phoneId)
+        return new TelephonyTimeSuggestion.Builder(slotIndex)
                 .setUtcTime(utcTime)
                 .build();
     }
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
index 3e7d40a..039c2b4 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -29,7 +29,7 @@
 import static org.mockito.Mockito.when;
 
 import android.app.timezonedetector.ManualTimeZoneSuggestion;
-import android.app.timezonedetector.PhoneTimeZoneSuggestion;
+import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.HandlerThread;
@@ -76,35 +76,35 @@
     }
 
     @Test(expected = SecurityException.class)
-    public void testSuggestPhoneTime_withoutPermission() {
+    public void testSuggestTelephonyTime_withoutPermission() {
         doThrow(new SecurityException("Mock"))
                 .when(mMockContext).enforceCallingPermission(anyString(), any());
-        PhoneTimeZoneSuggestion timeZoneSuggestion = createPhoneTimeZoneSuggestion();
+        TelephonyTimeZoneSuggestion timeZoneSuggestion = createTelephonyTimeZoneSuggestion();
 
         try {
-            mTimeZoneDetectorService.suggestPhoneTimeZone(timeZoneSuggestion);
+            mTimeZoneDetectorService.suggestTelephonyTimeZone(timeZoneSuggestion);
             fail();
         } finally {
             verify(mMockContext).enforceCallingPermission(
-                    eq(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE),
+                    eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE),
                     anyString());
         }
     }
 
     @Test
-    public void testSuggestPhoneTimeZone() throws Exception {
+    public void testSuggestTelephonyTimeZone() throws Exception {
         doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
 
-        PhoneTimeZoneSuggestion timeZoneSuggestion = createPhoneTimeZoneSuggestion();
-        mTimeZoneDetectorService.suggestPhoneTimeZone(timeZoneSuggestion);
+        TelephonyTimeZoneSuggestion timeZoneSuggestion = createTelephonyTimeZoneSuggestion();
+        mTimeZoneDetectorService.suggestTelephonyTimeZone(timeZoneSuggestion);
         mTestHandler.assertTotalMessagesEnqueued(1);
 
         verify(mMockContext).enforceCallingPermission(
-                eq(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE),
+                eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE),
                 anyString());
 
         mTestHandler.waitForMessagesToBeProcessed();
-        mStubbedTimeZoneDetectorStrategy.verifySuggestPhoneTimeZoneCalled(timeZoneSuggestion);
+        mStubbedTimeZoneDetectorStrategy.verifySuggestTelephonyTimeZoneCalled(timeZoneSuggestion);
     }
 
     @Test(expected = SecurityException.class)
@@ -165,12 +165,12 @@
         mStubbedTimeZoneDetectorStrategy.verifyHandleAutoTimeZoneDetectionChangedCalled();
     }
 
-    private static PhoneTimeZoneSuggestion createPhoneTimeZoneSuggestion() {
+    private static TelephonyTimeZoneSuggestion createTelephonyTimeZoneSuggestion() {
         int slotIndex = 1234;
-        return new PhoneTimeZoneSuggestion.Builder(slotIndex)
+        return new TelephonyTimeZoneSuggestion.Builder(slotIndex)
                 .setZoneId("TestZoneId")
-                .setMatchType(PhoneTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET)
-                .setQuality(PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE)
+                .setMatchType(TelephonyTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET)
+                .setQuality(TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE)
                 .build();
     }
 
@@ -181,14 +181,14 @@
     private static class StubbedTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy {
 
         // Call tracking.
-        private PhoneTimeZoneSuggestion mLastPhoneSuggestion;
+        private TelephonyTimeZoneSuggestion mLastTelephonySuggestion;
         private ManualTimeZoneSuggestion mLastManualSuggestion;
         private boolean mHandleAutoTimeZoneDetectionChangedCalled;
         private boolean mDumpCalled;
 
         @Override
-        public void suggestPhoneTimeZone(PhoneTimeZoneSuggestion timeZoneSuggestion) {
-            mLastPhoneSuggestion = timeZoneSuggestion;
+        public void suggestTelephonyTimeZone(TelephonyTimeZoneSuggestion timeZoneSuggestion) {
+            mLastTelephonySuggestion = timeZoneSuggestion;
         }
 
         @Override
@@ -207,14 +207,14 @@
         }
 
         void resetCallTracking() {
-            mLastPhoneSuggestion = null;
+            mLastTelephonySuggestion = null;
             mLastManualSuggestion = null;
             mHandleAutoTimeZoneDetectionChangedCalled = false;
             mDumpCalled = false;
         }
 
-        void verifySuggestPhoneTimeZoneCalled(PhoneTimeZoneSuggestion expectedSuggestion) {
-            assertEquals(expectedSuggestion, mLastPhoneSuggestion);
+        void verifySuggestTelephonyTimeZoneCalled(TelephonyTimeZoneSuggestion expectedSuggestion) {
+            assertEquals(expectedSuggestion, mLastTelephonySuggestion);
         }
 
         public void verifySuggestManualTimeZoneCalled(ManualTimeZoneSuggestion expectedSuggestion) {
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index 1e38711..ba30967 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -16,20 +16,20 @@
 
 package com.android.server.timezonedetector;
 
-import static android.app.timezonedetector.PhoneTimeZoneSuggestion.MATCH_TYPE_EMULATOR_ZONE_ID;
-import static android.app.timezonedetector.PhoneTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET;
-import static android.app.timezonedetector.PhoneTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_ONLY;
-import static android.app.timezonedetector.PhoneTimeZoneSuggestion.MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY;
-import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS;
-import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET;
-import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE;
+import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.MATCH_TYPE_EMULATOR_ZONE_ID;
+import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET;
+import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_ONLY;
+import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY;
+import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS;
+import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET;
+import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE;
 
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.PHONE_SCORE_HIGH;
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.PHONE_SCORE_HIGHEST;
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.PHONE_SCORE_LOW;
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.PHONE_SCORE_MEDIUM;
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.PHONE_SCORE_NONE;
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.PHONE_SCORE_USAGE_THRESHOLD;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.TELEPHONY_SCORE_HIGH;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.TELEPHONY_SCORE_HIGHEST;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.TELEPHONY_SCORE_LOW;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.TELEPHONY_SCORE_MEDIUM;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.TELEPHONY_SCORE_NONE;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.TELEPHONY_SCORE_USAGE_THRESHOLD;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -37,11 +37,11 @@
 import static org.junit.Assert.assertTrue;
 
 import android.app.timezonedetector.ManualTimeZoneSuggestion;
-import android.app.timezonedetector.PhoneTimeZoneSuggestion;
-import android.app.timezonedetector.PhoneTimeZoneSuggestion.MatchType;
-import android.app.timezonedetector.PhoneTimeZoneSuggestion.Quality;
+import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
+import android.app.timezonedetector.TelephonyTimeZoneSuggestion.MatchType;
+import android.app.timezonedetector.TelephonyTimeZoneSuggestion.Quality;
 
-import com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.QualifiedPhoneTimeZoneSuggestion;
+import com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.QualifiedTelephonyTimeZoneSuggestion;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -58,24 +58,24 @@
 
     /** A time zone used for initialization that does not occur elsewhere in tests. */
     private static final String ARBITRARY_TIME_ZONE_ID = "Etc/UTC";
-    private static final int PHONE1_ID = 10000;
-    private static final int PHONE2_ID = 20000;
+    private static final int SLOT_INDEX1 = 10000;
+    private static final int SLOT_INDEX2 = 20000;
 
     // Suggestion test cases are ordered so that each successive one is of the same or higher score
     // than the previous.
     private static final SuggestionTestCase[] TEST_CASES = new SuggestionTestCase[] {
             newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY,
-                    QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS, PHONE_SCORE_LOW),
+                    QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS, TELEPHONY_SCORE_LOW),
             newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY, QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET,
-                    PHONE_SCORE_MEDIUM),
+                    TELEPHONY_SCORE_MEDIUM),
             newTestCase(MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET,
-                    QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET, PHONE_SCORE_MEDIUM),
-            newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY, QUALITY_SINGLE_ZONE, PHONE_SCORE_HIGH),
+                    QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET, TELEPHONY_SCORE_MEDIUM),
+            newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY, QUALITY_SINGLE_ZONE, TELEPHONY_SCORE_HIGH),
             newTestCase(MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE,
-                    PHONE_SCORE_HIGH),
+                    TELEPHONY_SCORE_HIGH),
             newTestCase(MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY,
-                    QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET, PHONE_SCORE_HIGHEST),
-            newTestCase(MATCH_TYPE_EMULATOR_ZONE_ID, QUALITY_SINGLE_ZONE, PHONE_SCORE_HIGHEST),
+                    QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET, TELEPHONY_SCORE_HIGHEST),
+            newTestCase(MATCH_TYPE_EMULATOR_ZONE_ID, QUALITY_SINGLE_ZONE, TELEPHONY_SCORE_HIGHEST),
     };
 
     private TimeZoneDetectorStrategyImpl mTimeZoneDetectorStrategy;
@@ -89,76 +89,82 @@
     }
 
     @Test
-    public void testEmptyPhoneSuggestions() {
-        PhoneTimeZoneSuggestion phone1TimeZoneSuggestion = createEmptyPhone1Suggestion();
-        PhoneTimeZoneSuggestion phone2TimeZoneSuggestion = createEmptyPhone2Suggestion();
+    public void testEmptyTelephonySuggestions() {
+        TelephonyTimeZoneSuggestion slotIndex1TimeZoneSuggestion =
+                createEmptySlotIndex1Suggestion();
+        TelephonyTimeZoneSuggestion slotIndex2TimeZoneSuggestion =
+                createEmptySlotIndex2Suggestion();
         Script script = new Script()
                 .initializeAutoTimeZoneDetection(true)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
-        script.suggestPhoneTimeZone(phone1TimeZoneSuggestion)
+        script.suggestTelephonyTimeZone(slotIndex1TimeZoneSuggestion)
                 .verifyTimeZoneNotSet();
 
         // Assert internal service state.
-        QualifiedPhoneTimeZoneSuggestion expectedPhone1ScoredSuggestion =
-                new QualifiedPhoneTimeZoneSuggestion(phone1TimeZoneSuggestion, PHONE_SCORE_NONE);
-        assertEquals(expectedPhone1ScoredSuggestion,
-                mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
-        assertNull(mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE2_ID));
-        assertEquals(expectedPhone1ScoredSuggestion,
-                mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
+        QualifiedTelephonyTimeZoneSuggestion expectedSlotIndex1ScoredSuggestion =
+                new QualifiedTelephonyTimeZoneSuggestion(slotIndex1TimeZoneSuggestion,
+                        TELEPHONY_SCORE_NONE);
+        assertEquals(expectedSlotIndex1ScoredSuggestion,
+                mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1));
+        assertNull(mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX2));
+        assertEquals(expectedSlotIndex1ScoredSuggestion,
+                mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
 
-        script.suggestPhoneTimeZone(phone2TimeZoneSuggestion)
+        script.suggestTelephonyTimeZone(slotIndex2TimeZoneSuggestion)
                 .verifyTimeZoneNotSet();
 
         // Assert internal service state.
-        QualifiedPhoneTimeZoneSuggestion expectedPhone2ScoredSuggestion =
-                new QualifiedPhoneTimeZoneSuggestion(phone2TimeZoneSuggestion, PHONE_SCORE_NONE);
-        assertEquals(expectedPhone1ScoredSuggestion,
-                mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
-        assertEquals(expectedPhone2ScoredSuggestion,
-                mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE2_ID));
-        // Phone 1 should always beat phone 2, all other things being equal.
-        assertEquals(expectedPhone1ScoredSuggestion,
-                mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
+        QualifiedTelephonyTimeZoneSuggestion expectedSlotIndex2ScoredSuggestion =
+                new QualifiedTelephonyTimeZoneSuggestion(slotIndex2TimeZoneSuggestion,
+                        TELEPHONY_SCORE_NONE);
+        assertEquals(expectedSlotIndex1ScoredSuggestion,
+                mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1));
+        assertEquals(expectedSlotIndex2ScoredSuggestion,
+                mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX2));
+        // SlotIndex1 should always beat slotIndex2, all other things being equal.
+        assertEquals(expectedSlotIndex1ScoredSuggestion,
+                mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
     }
 
     @Test
-    public void testFirstPlausiblePhoneSuggestionAcceptedWhenTimeZoneUninitialized() {
+    public void testFirstPlausibleTelephonySuggestionAcceptedWhenTimeZoneUninitialized() {
         SuggestionTestCase testCase = newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY,
-                QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS, PHONE_SCORE_LOW);
-        PhoneTimeZoneSuggestion lowQualitySuggestion =
-                testCase.createSuggestion(PHONE1_ID, "America/New_York");
+                QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS, TELEPHONY_SCORE_LOW);
+        TelephonyTimeZoneSuggestion lowQualitySuggestion =
+                testCase.createSuggestion(SLOT_INDEX1, "America/New_York");
 
         // The device time zone setting is left uninitialized.
         Script script = new Script()
                 .initializeAutoTimeZoneDetection(true);
 
         // The very first suggestion will be taken.
-        script.suggestPhoneTimeZone(lowQualitySuggestion)
+        script.suggestTelephonyTimeZone(lowQualitySuggestion)
                 .verifyTimeZoneSetAndReset(lowQualitySuggestion);
 
         // Assert internal service state.
-        QualifiedPhoneTimeZoneSuggestion expectedScoredSuggestion =
-                new QualifiedPhoneTimeZoneSuggestion(lowQualitySuggestion, testCase.expectedScore);
+        QualifiedTelephonyTimeZoneSuggestion expectedScoredSuggestion =
+                new QualifiedTelephonyTimeZoneSuggestion(
+                        lowQualitySuggestion, testCase.expectedScore);
         assertEquals(expectedScoredSuggestion,
-                mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
+                mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1));
         assertEquals(expectedScoredSuggestion,
-                mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
+                mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
 
         // Another low quality suggestion will be ignored now that the setting is initialized.
-        PhoneTimeZoneSuggestion lowQualitySuggestion2 =
-                testCase.createSuggestion(PHONE1_ID, "America/Los_Angeles");
-        script.suggestPhoneTimeZone(lowQualitySuggestion2)
+        TelephonyTimeZoneSuggestion lowQualitySuggestion2 =
+                testCase.createSuggestion(SLOT_INDEX1, "America/Los_Angeles");
+        script.suggestTelephonyTimeZone(lowQualitySuggestion2)
                 .verifyTimeZoneNotSet();
 
         // Assert internal service state.
-        QualifiedPhoneTimeZoneSuggestion expectedScoredSuggestion2 =
-                new QualifiedPhoneTimeZoneSuggestion(lowQualitySuggestion2, testCase.expectedScore);
+        QualifiedTelephonyTimeZoneSuggestion expectedScoredSuggestion2 =
+                new QualifiedTelephonyTimeZoneSuggestion(
+                        lowQualitySuggestion2, testCase.expectedScore);
         assertEquals(expectedScoredSuggestion2,
-                mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
+                mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1));
         assertEquals(expectedScoredSuggestion2,
-                mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
+                mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
     }
 
     /**
@@ -174,28 +180,28 @@
             script.initializeAutoTimeZoneDetection(false)
                     .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
-            PhoneTimeZoneSuggestion suggestion =
-                    testCase.createSuggestion(PHONE1_ID, "Europe/London");
-            script.suggestPhoneTimeZone(suggestion);
+            TelephonyTimeZoneSuggestion suggestion =
+                    testCase.createSuggestion(SLOT_INDEX1, "Europe/London");
+            script.suggestTelephonyTimeZone(suggestion);
 
             // When time zone detection is not enabled, the time zone suggestion will not be set
             // regardless of the score.
             script.verifyTimeZoneNotSet();
 
             // Assert internal service state.
-            QualifiedPhoneTimeZoneSuggestion expectedScoredSuggestion =
-                    new QualifiedPhoneTimeZoneSuggestion(suggestion, testCase.expectedScore);
+            QualifiedTelephonyTimeZoneSuggestion expectedScoredSuggestion =
+                    new QualifiedTelephonyTimeZoneSuggestion(suggestion, testCase.expectedScore);
             assertEquals(expectedScoredSuggestion,
-                    mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
+                    mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1));
             assertEquals(expectedScoredSuggestion,
-                    mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
+                    mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
 
             // Toggling the time zone setting on should cause the device setting to be set.
             script.autoTimeZoneDetectionEnabled(true);
 
             // When time zone detection is already enabled the suggestion (if it scores highly
             // enough) should be set immediately.
-            if (testCase.expectedScore >= PHONE_SCORE_USAGE_THRESHOLD) {
+            if (testCase.expectedScore >= TELEPHONY_SCORE_USAGE_THRESHOLD) {
                 script.verifyTimeZoneSetAndReset(suggestion);
             } else {
                 script.verifyTimeZoneNotSet();
@@ -203,9 +209,9 @@
 
             // Assert internal service state.
             assertEquals(expectedScoredSuggestion,
-                    mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
+                    mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1));
             assertEquals(expectedScoredSuggestion,
-                    mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
+                    mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
 
             // Toggling the time zone setting should off should do nothing.
             script.autoTimeZoneDetectionEnabled(false)
@@ -213,20 +219,20 @@
 
             // Assert internal service state.
             assertEquals(expectedScoredSuggestion,
-                    mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
+                    mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1));
             assertEquals(expectedScoredSuggestion,
-                    mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
+                    mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
         }
     }
 
     @Test
-    public void testPhoneSuggestionsSinglePhone() {
+    public void testTelephonySuggestionsSingleSlotId() {
         Script script = new Script()
                 .initializeAutoTimeZoneDetection(true)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         for (SuggestionTestCase testCase : TEST_CASES) {
-            makePhone1SuggestionAndCheckState(script, testCase);
+            makeSlotIndex1SuggestionAndCheckState(script, testCase);
         }
 
         /*
@@ -241,125 +247,128 @@
         Collections.reverse(descendingCasesByScore);
 
         for (SuggestionTestCase testCase : descendingCasesByScore) {
-            makePhone1SuggestionAndCheckState(script, testCase);
+            makeSlotIndex1SuggestionAndCheckState(script, testCase);
         }
     }
 
-    private void makePhone1SuggestionAndCheckState(Script script, SuggestionTestCase testCase) {
+    private void makeSlotIndex1SuggestionAndCheckState(Script script, SuggestionTestCase testCase) {
         // Give the next suggestion a different zone from the currently set device time zone;
         String currentZoneId = mFakeTimeZoneDetectorStrategyCallback.getDeviceTimeZone();
         String suggestionZoneId =
                 "Europe/London".equals(currentZoneId) ? "Europe/Paris" : "Europe/London";
-        PhoneTimeZoneSuggestion zonePhone1Suggestion =
-                testCase.createSuggestion(PHONE1_ID, suggestionZoneId);
-        QualifiedPhoneTimeZoneSuggestion expectedZonePhone1ScoredSuggestion =
-                new QualifiedPhoneTimeZoneSuggestion(zonePhone1Suggestion, testCase.expectedScore);
+        TelephonyTimeZoneSuggestion zoneSlotIndex1Suggestion =
+                testCase.createSuggestion(SLOT_INDEX1, suggestionZoneId);
+        QualifiedTelephonyTimeZoneSuggestion expectedZoneSlotIndex1ScoredSuggestion =
+                new QualifiedTelephonyTimeZoneSuggestion(
+                        zoneSlotIndex1Suggestion, testCase.expectedScore);
 
-        script.suggestPhoneTimeZone(zonePhone1Suggestion);
-        if (testCase.expectedScore >= PHONE_SCORE_USAGE_THRESHOLD) {
-            script.verifyTimeZoneSetAndReset(zonePhone1Suggestion);
+        script.suggestTelephonyTimeZone(zoneSlotIndex1Suggestion);
+        if (testCase.expectedScore >= TELEPHONY_SCORE_USAGE_THRESHOLD) {
+            script.verifyTimeZoneSetAndReset(zoneSlotIndex1Suggestion);
         } else {
             script.verifyTimeZoneNotSet();
         }
 
         // Assert internal service state.
-        assertEquals(expectedZonePhone1ScoredSuggestion,
-                mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
-        assertEquals(expectedZonePhone1ScoredSuggestion,
-                mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
+        assertEquals(expectedZoneSlotIndex1ScoredSuggestion,
+                mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1));
+        assertEquals(expectedZoneSlotIndex1ScoredSuggestion,
+                mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
     }
 
     /**
-     * Tries a set of test cases to see if the phone with the lowest ID is given preference. This
-     * test also confirms that the time zone setting would only be set if a suggestion is of
-     * sufficient quality.
+     * Tries a set of test cases to see if the slotIndex with the lowest numeric value is given
+     * preference. This test also confirms that the time zone setting would only be set if a
+     * suggestion is of sufficient quality.
      */
     @Test
-    public void testMultiplePhoneSuggestionScoringAndPhoneIdBias() {
+    public void testMultipleSlotIndexSuggestionScoringAndSlotIndexBias() {
         String[] zoneIds = { "Europe/London", "Europe/Paris" };
-        PhoneTimeZoneSuggestion emptyPhone1Suggestion = createEmptyPhone1Suggestion();
-        PhoneTimeZoneSuggestion emptyPhone2Suggestion = createEmptyPhone2Suggestion();
-        QualifiedPhoneTimeZoneSuggestion expectedEmptyPhone1ScoredSuggestion =
-                new QualifiedPhoneTimeZoneSuggestion(emptyPhone1Suggestion, PHONE_SCORE_NONE);
-        QualifiedPhoneTimeZoneSuggestion expectedEmptyPhone2ScoredSuggestion =
-                new QualifiedPhoneTimeZoneSuggestion(emptyPhone2Suggestion, PHONE_SCORE_NONE);
+        TelephonyTimeZoneSuggestion emptySlotIndex1Suggestion = createEmptySlotIndex1Suggestion();
+        TelephonyTimeZoneSuggestion emptySlotIndex2Suggestion = createEmptySlotIndex2Suggestion();
+        QualifiedTelephonyTimeZoneSuggestion expectedEmptySlotIndex1ScoredSuggestion =
+                new QualifiedTelephonyTimeZoneSuggestion(emptySlotIndex1Suggestion,
+                        TELEPHONY_SCORE_NONE);
+        QualifiedTelephonyTimeZoneSuggestion expectedEmptySlotIndex2ScoredSuggestion =
+                new QualifiedTelephonyTimeZoneSuggestion(emptySlotIndex2Suggestion,
+                        TELEPHONY_SCORE_NONE);
 
         Script script = new Script()
                 .initializeAutoTimeZoneDetection(true)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
                 // Initialize the latest suggestions as empty so we don't need to worry about nulls
                 // below for the first loop.
-                .suggestPhoneTimeZone(emptyPhone1Suggestion)
-                .suggestPhoneTimeZone(emptyPhone2Suggestion)
+                .suggestTelephonyTimeZone(emptySlotIndex1Suggestion)
+                .suggestTelephonyTimeZone(emptySlotIndex2Suggestion)
                 .resetState();
 
         for (SuggestionTestCase testCase : TEST_CASES) {
-            PhoneTimeZoneSuggestion zonePhone1Suggestion =
-                    testCase.createSuggestion(PHONE1_ID, zoneIds[0]);
-            PhoneTimeZoneSuggestion zonePhone2Suggestion =
-                    testCase.createSuggestion(PHONE2_ID, zoneIds[1]);
-            QualifiedPhoneTimeZoneSuggestion expectedZonePhone1ScoredSuggestion =
-                    new QualifiedPhoneTimeZoneSuggestion(zonePhone1Suggestion,
+            TelephonyTimeZoneSuggestion zoneSlotIndex1Suggestion =
+                    testCase.createSuggestion(SLOT_INDEX1, zoneIds[0]);
+            TelephonyTimeZoneSuggestion zoneSlotIndex2Suggestion =
+                    testCase.createSuggestion(SLOT_INDEX2, zoneIds[1]);
+            QualifiedTelephonyTimeZoneSuggestion expectedZoneSlotIndex1ScoredSuggestion =
+                    new QualifiedTelephonyTimeZoneSuggestion(zoneSlotIndex1Suggestion,
                             testCase.expectedScore);
-            QualifiedPhoneTimeZoneSuggestion expectedZonePhone2ScoredSuggestion =
-                    new QualifiedPhoneTimeZoneSuggestion(zonePhone2Suggestion,
+            QualifiedTelephonyTimeZoneSuggestion expectedZoneSlotIndex2ScoredSuggestion =
+                    new QualifiedTelephonyTimeZoneSuggestion(zoneSlotIndex2Suggestion,
                             testCase.expectedScore);
 
-            // Start the test by making a suggestion for phone 1.
-            script.suggestPhoneTimeZone(zonePhone1Suggestion);
-            if (testCase.expectedScore >= PHONE_SCORE_USAGE_THRESHOLD) {
-                script.verifyTimeZoneSetAndReset(zonePhone1Suggestion);
+            // Start the test by making a suggestion for slotIndex1.
+            script.suggestTelephonyTimeZone(zoneSlotIndex1Suggestion);
+            if (testCase.expectedScore >= TELEPHONY_SCORE_USAGE_THRESHOLD) {
+                script.verifyTimeZoneSetAndReset(zoneSlotIndex1Suggestion);
             } else {
                 script.verifyTimeZoneNotSet();
             }
 
             // Assert internal service state.
-            assertEquals(expectedZonePhone1ScoredSuggestion,
-                    mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
-            assertEquals(expectedEmptyPhone2ScoredSuggestion,
-                    mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE2_ID));
-            assertEquals(expectedZonePhone1ScoredSuggestion,
-                    mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
+            assertEquals(expectedZoneSlotIndex1ScoredSuggestion,
+                    mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1));
+            assertEquals(expectedEmptySlotIndex2ScoredSuggestion,
+                    mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX2));
+            assertEquals(expectedZoneSlotIndex1ScoredSuggestion,
+                    mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
 
-            // Phone 2 then makes an alternative suggestion with an identical score. Phone 1's
+            // SlotIndex2 then makes an alternative suggestion with an identical score. SlotIndex1's
             // suggestion should still "win" if it is above the required threshold.
-            script.suggestPhoneTimeZone(zonePhone2Suggestion);
+            script.suggestTelephonyTimeZone(zoneSlotIndex2Suggestion);
             script.verifyTimeZoneNotSet();
 
             // Assert internal service state.
-            assertEquals(expectedZonePhone1ScoredSuggestion,
-                    mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
-            assertEquals(expectedZonePhone2ScoredSuggestion,
-                    mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE2_ID));
-            // Phone 1 should always beat phone 2, all other things being equal.
-            assertEquals(expectedZonePhone1ScoredSuggestion,
-                    mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
+            assertEquals(expectedZoneSlotIndex1ScoredSuggestion,
+                    mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1));
+            assertEquals(expectedZoneSlotIndex2ScoredSuggestion,
+                    mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX2));
+            // SlotIndex1 should always beat slotIndex2, all other things being equal.
+            assertEquals(expectedZoneSlotIndex1ScoredSuggestion,
+                    mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
 
-            // Withdrawing phone 1's suggestion should leave phone 2 as the new winner. Since the
-            // zoneId is different, the time zone setting should be updated if the score is high
+            // Withdrawing slotIndex1's suggestion should leave slotIndex2 as the new winner. Since
+            // the zoneId is different, the time zone setting should be updated if the score is high
             // enough.
-            script.suggestPhoneTimeZone(emptyPhone1Suggestion);
-            if (testCase.expectedScore >= PHONE_SCORE_USAGE_THRESHOLD) {
-                script.verifyTimeZoneSetAndReset(zonePhone2Suggestion);
+            script.suggestTelephonyTimeZone(emptySlotIndex1Suggestion);
+            if (testCase.expectedScore >= TELEPHONY_SCORE_USAGE_THRESHOLD) {
+                script.verifyTimeZoneSetAndReset(zoneSlotIndex2Suggestion);
             } else {
                 script.verifyTimeZoneNotSet();
             }
 
             // Assert internal service state.
-            assertEquals(expectedEmptyPhone1ScoredSuggestion,
-                    mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
-            assertEquals(expectedZonePhone2ScoredSuggestion,
-                    mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE2_ID));
-            assertEquals(expectedZonePhone2ScoredSuggestion,
-                    mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
+            assertEquals(expectedEmptySlotIndex1ScoredSuggestion,
+                    mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1));
+            assertEquals(expectedZoneSlotIndex2ScoredSuggestion,
+                    mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX2));
+            assertEquals(expectedZoneSlotIndex2ScoredSuggestion,
+                    mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
 
             // Reset the state for the next loop.
-            script.suggestPhoneTimeZone(emptyPhone2Suggestion)
+            script.suggestTelephonyTimeZone(emptySlotIndex2Suggestion)
                     .verifyTimeZoneNotSet();
-            assertEquals(expectedEmptyPhone1ScoredSuggestion,
-                    mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
-            assertEquals(expectedEmptyPhone2ScoredSuggestion,
-                    mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE2_ID));
+            assertEquals(expectedEmptySlotIndex1ScoredSuggestion,
+                    mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1));
+            assertEquals(expectedEmptySlotIndex2ScoredSuggestion,
+                    mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX2));
         }
     }
 
@@ -375,21 +384,21 @@
 
         SuggestionTestCase testCase =
                 newTestCase(MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE,
-                        PHONE_SCORE_HIGH);
-        PhoneTimeZoneSuggestion losAngelesSuggestion =
-                testCase.createSuggestion(PHONE1_ID, "America/Los_Angeles");
-        PhoneTimeZoneSuggestion newYorkSuggestion =
-                testCase.createSuggestion(PHONE1_ID, "America/New_York");
+                        TELEPHONY_SCORE_HIGH);
+        TelephonyTimeZoneSuggestion losAngelesSuggestion =
+                testCase.createSuggestion(SLOT_INDEX1, "America/Los_Angeles");
+        TelephonyTimeZoneSuggestion newYorkSuggestion =
+                testCase.createSuggestion(SLOT_INDEX1, "America/New_York");
 
         // Initialization.
-        script.suggestPhoneTimeZone(losAngelesSuggestion)
+        script.suggestTelephonyTimeZone(losAngelesSuggestion)
                 .verifyTimeZoneSetAndReset(losAngelesSuggestion);
         // Suggest it again - it should not be set because it is already set.
-        script.suggestPhoneTimeZone(losAngelesSuggestion)
+        script.suggestTelephonyTimeZone(losAngelesSuggestion)
                 .verifyTimeZoneNotSet();
 
         // Toggling time zone detection should set the device time zone only if the current setting
-        // value is different from the most recent phone suggestion.
+        // value is different from the most recent telephony suggestion.
         script.autoTimeZoneDetectionEnabled(false)
                 .verifyTimeZoneNotSet()
                 .autoTimeZoneDetectionEnabled(true)
@@ -398,7 +407,7 @@
         // Simulate a user turning auto detection off, a new suggestion being made while auto
         // detection is off, and the user turning it on again.
         script.autoTimeZoneDetectionEnabled(false)
-                .suggestPhoneTimeZone(newYorkSuggestion)
+                .suggestTelephonyTimeZone(newYorkSuggestion)
                 .verifyTimeZoneNotSet();
         // Latest suggestion should be used.
         script.autoTimeZoneDetectionEnabled(true)
@@ -433,12 +442,12 @@
         return new ManualTimeZoneSuggestion(zoneId);
     }
 
-    private static PhoneTimeZoneSuggestion createEmptyPhone1Suggestion() {
-        return new PhoneTimeZoneSuggestion.Builder(PHONE1_ID).build();
+    private static TelephonyTimeZoneSuggestion createEmptySlotIndex1Suggestion() {
+        return new TelephonyTimeZoneSuggestion.Builder(SLOT_INDEX1).build();
     }
 
-    private static PhoneTimeZoneSuggestion createEmptyPhone2Suggestion() {
-        return new PhoneTimeZoneSuggestion.Builder(PHONE2_ID).build();
+    private static TelephonyTimeZoneSuggestion createEmptySlotIndex2Suggestion() {
+        return new TelephonyTimeZoneSuggestion.Builder(SLOT_INDEX2).build();
     }
 
     static class FakeTimeZoneDetectorStrategyCallback
@@ -565,9 +574,11 @@
             return this;
         }
 
-        /** Simulates the time zone detection strategy receiving a phone-originated suggestion. */
-        Script suggestPhoneTimeZone(PhoneTimeZoneSuggestion phoneTimeZoneSuggestion) {
-            mTimeZoneDetectorStrategy.suggestPhoneTimeZone(phoneTimeZoneSuggestion);
+        /**
+         * Simulates the time zone detection strategy receiving a telephony-originated suggestion.
+         */
+        Script suggestTelephonyTimeZone(TelephonyTimeZoneSuggestion timeZoneSuggestion) {
+            mTimeZoneDetectorStrategy.suggestTelephonyTimeZone(timeZoneSuggestion);
             return this;
         }
 
@@ -582,7 +593,7 @@
             return this;
         }
 
-        Script verifyTimeZoneSetAndReset(PhoneTimeZoneSuggestion suggestion) {
+        Script verifyTimeZoneSetAndReset(TelephonyTimeZoneSuggestion suggestion) {
             mFakeTimeZoneDetectorStrategyCallback.assertTimeZoneSet(suggestion.getZoneId());
             mFakeTimeZoneDetectorStrategyCallback.commitAllChanges();
             return this;
@@ -611,8 +622,8 @@
             this.expectedScore = expectedScore;
         }
 
-        private PhoneTimeZoneSuggestion createSuggestion(int phoneId, String zoneId) {
-            return new PhoneTimeZoneSuggestion.Builder(phoneId)
+        private TelephonyTimeZoneSuggestion createSuggestion(int slotIndex, String zoneId) {
+            return new TelephonyTimeZoneSuggestion.Builder(slotIndex)
                     .setZoneId(zoneId)
                     .setMatchType(matchType)
                     .setQuality(quality)
diff --git a/services/tests/uiservicestests/AndroidManifest.xml b/services/tests/uiservicestests/AndroidManifest.xml
index 180deb5..dab0a5f 100644
--- a/services/tests/uiservicestests/AndroidManifest.xml
+++ b/services/tests/uiservicestests/AndroidManifest.xml
@@ -28,6 +28,8 @@
     <uses-permission android:name="android.permission.ACCESS_VOICE_INTERACTION_SERVICE" />
     <uses-permission android:name="android.permission.DEVICE_POWER" />
     <uses-permission android:name="android.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY" />
+    <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/>
+    <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/>
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.OBSERVE_ROLE_HOLDERS" />
     <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT"/>
diff --git a/tests/BlobStoreTestUtils/Android.bp b/tests/BlobStoreTestUtils/Android.bp
new file mode 100644
index 0000000..edd2b43
--- /dev/null
+++ b/tests/BlobStoreTestUtils/Android.bp
@@ -0,0 +1,20 @@
+// Copyright (C) 2020 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.
+
+java_library {
+  name: "BlobStoreTestUtils",
+  srcs: ["src/**/*.java"],
+  static_libs: ["truth-prebuilt"],
+  platform_apis: true
+}
\ No newline at end of file
diff --git a/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java b/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java
new file mode 100644
index 0000000..f96766a
--- /dev/null
+++ b/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2020 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.utils.blob;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.blob.BlobHandle;
+import android.app.blob.BlobStoreManager;
+import android.content.Context;
+import android.os.FileUtils;
+import android.os.ParcelFileDescriptor;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.nio.file.Files;
+import java.security.MessageDigest;
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
+
+public class DummyBlobData {
+    private static final long DEFAULT_SIZE_BYTES = 10 * 1024L * 1024L;
+    private static final int BUFFER_SIZE_BYTES = 16 * 1024;
+
+    private final Context mContext;
+    private final Random mRandom;
+    private final File mFile;
+    private final long mFileSize;
+    private final String mLabel;
+
+    byte[] mFileDigest;
+    long mExpiryTimeMs;
+
+    public DummyBlobData(Context context) {
+        this(context, new Random(0), "blob_" + System.nanoTime());
+    }
+
+    public DummyBlobData(Context context, long fileSize) {
+        this(context, fileSize, new Random(0), "blob_" + System.nanoTime(), "Test label");
+    }
+
+    public DummyBlobData(Context context, Random random, String fileName) {
+        this(context, DEFAULT_SIZE_BYTES, random, fileName, "Test label");
+    }
+
+    public DummyBlobData(Context context, Random random, String fileName, String label) {
+        this(context, DEFAULT_SIZE_BYTES, random, fileName, label);
+    }
+
+    public DummyBlobData(Context context, long fileSize, Random random, String fileName,
+            String label) {
+        mContext = context;
+        mRandom = random;
+        mFile = new File(mContext.getFilesDir(), fileName);
+        mFileSize = fileSize;
+        mLabel = label;
+    }
+
+    public void prepare() throws Exception {
+        try (RandomAccessFile file = new RandomAccessFile(mFile, "rw")) {
+            writeRandomData(file, mFileSize);
+        }
+        mFileDigest = FileUtils.digest(mFile, "SHA-256");
+        mExpiryTimeMs = System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1);
+    }
+
+    public BlobHandle getBlobHandle() throws Exception {
+        return BlobHandle.createWithSha256(createSha256Digest(mFile), mLabel,
+                mExpiryTimeMs, "test_tag");
+    }
+
+    public long getFileSize() throws Exception {
+        return mFileSize;
+    }
+
+    public long getExpiryTimeMillis() {
+        return mExpiryTimeMs;
+    }
+
+    public void delete() {
+        mFile.delete();
+    }
+
+    public void writeToSession(BlobStoreManager.Session session) throws Exception {
+        writeToSession(session, 0, mFileSize);
+    }
+
+    public void writeToSession(BlobStoreManager.Session session,
+            long offsetBytes, long lengthBytes) throws Exception {
+        try (FileInputStream in = new FileInputStream(mFile)) {
+            in.getChannel().position(offsetBytes);
+            try (FileOutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream(
+                    session.openWrite(offsetBytes, lengthBytes))) {
+                copy(in, out, lengthBytes);
+            }
+        }
+    }
+
+    public void writeToFd(FileDescriptor fd, long offsetBytes, long lengthBytes) throws Exception {
+        try (FileInputStream in = new FileInputStream(mFile)) {
+            in.getChannel().position(offsetBytes);
+            try (FileOutputStream out = new FileOutputStream(fd)) {
+                copy(in, out, lengthBytes);
+            }
+        }
+    }
+
+    private void copy(InputStream in, OutputStream out, long lengthBytes) throws Exception {
+        final byte[] buffer = new byte[BUFFER_SIZE_BYTES];
+        long bytesWrittern = 0;
+        while (bytesWrittern < lengthBytes) {
+            final int toWrite = (bytesWrittern + buffer.length <= lengthBytes)
+                    ? buffer.length : (int) (lengthBytes - bytesWrittern);
+            in.read(buffer, 0, toWrite);
+            out.write(buffer, 0, toWrite);
+            bytesWrittern += toWrite;
+        }
+    }
+
+    public void readFromSessionAndVerifyBytes(BlobStoreManager.Session session,
+            long offsetBytes, int lengthBytes) throws Exception {
+        final byte[] expectedBytes = new byte[lengthBytes];
+        try (FileInputStream in = new FileInputStream(mFile)) {
+            read(in, expectedBytes, offsetBytes, lengthBytes);
+        }
+
+        final byte[] actualBytes = new byte[lengthBytes];
+        try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(
+                session.openWrite(0L, 0L))) {
+            read(in, actualBytes, offsetBytes, lengthBytes);
+        }
+
+        assertThat(actualBytes).isEqualTo(expectedBytes);
+
+    }
+
+    private void read(FileInputStream in, byte[] buffer,
+            long offsetBytes, int lengthBytes) throws Exception {
+        in.getChannel().position(offsetBytes);
+        in.read(buffer, 0, lengthBytes);
+    }
+
+    public void readFromSessionAndVerifyDigest(BlobStoreManager.Session session)
+            throws Exception {
+        readFromSessionAndVerifyDigest(session, 0, mFile.length());
+    }
+
+    public void readFromSessionAndVerifyDigest(BlobStoreManager.Session session,
+            long offsetBytes, long lengthBytes) throws Exception {
+        final byte[] actualDigest;
+        try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(
+                session.openWrite(0L, 0L))) {
+            actualDigest = createSha256Digest(in, offsetBytes, lengthBytes);
+        }
+
+        assertThat(actualDigest).isEqualTo(mFileDigest);
+    }
+
+    public void verifyBlob(ParcelFileDescriptor pfd) throws Exception {
+        final byte[] actualDigest;
+        try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
+            actualDigest = FileUtils.digest(in, "SHA-256");
+        }
+        assertThat(actualDigest).isEqualTo(mFileDigest);
+    }
+
+    private byte[] createSha256Digest(FileInputStream in, long offsetBytes, long lengthBytes)
+            throws Exception {
+        final MessageDigest digest = MessageDigest.getInstance("SHA-256");
+        in.getChannel().position(offsetBytes);
+        final byte[] buffer = new byte[BUFFER_SIZE_BYTES];
+        long bytesRead = 0;
+        while (bytesRead < lengthBytes) {
+            int toRead = (bytesRead + buffer.length <= lengthBytes)
+                    ? buffer.length : (int) (lengthBytes - bytesRead);
+            toRead = in.read(buffer, 0, toRead);
+            digest.update(buffer, 0, toRead);
+            bytesRead += toRead;
+        }
+        return digest.digest();
+    }
+
+    private byte[] createSha256Digest(File file) throws Exception {
+        final MessageDigest digest = MessageDigest.getInstance("SHA-256");
+        try (BufferedInputStream in = new BufferedInputStream(
+                Files.newInputStream(file.toPath()))) {
+            final byte[] buffer = new byte[BUFFER_SIZE_BYTES];
+            int bytesRead;
+            while ((bytesRead = in.read(buffer)) > 0) {
+                digest.update(buffer, 0, bytesRead);
+            }
+        }
+        return digest.digest();
+    }
+
+    private void writeRandomData(RandomAccessFile file, long fileSize)
+            throws Exception {
+        long bytesWritten = 0;
+        final byte[] buffer = new byte[BUFFER_SIZE_BYTES];
+        while (bytesWritten < fileSize) {
+            mRandom.nextBytes(buffer);
+            final int toWrite = (bytesWritten + buffer.length <= fileSize)
+                    ? buffer.length : (int) (fileSize - bytesWritten);
+            file.seek(bytesWritten);
+            file.write(buffer, 0, toWrite);
+            bytesWritten += toWrite;
+        }
+    }
+}
diff --git a/tests/PlatformCompatGating/Android.bp b/tests/PlatformCompatGating/Android.bp
index 5e9ef8e..74dfde8 100644
--- a/tests/PlatformCompatGating/Android.bp
+++ b/tests/PlatformCompatGating/Android.bp
@@ -18,14 +18,11 @@
     name: "PlatformCompatGating",
     // Only compile source java files in this apk.
     srcs: ["src/**/*.java"],
-    certificate: "platform",
-    libs: [
-        "android.test.runner",
-        "android.test.base",
-    ],
     static_libs: [
         "junit",
-        "android-support-test",
+        "androidx.test.runner",
+        "androidx.test.core",
+        "androidx.test.ext.junit",
         "mockito-target-minus-junit4",
         "truth-prebuilt",
         "platform-compat-test-rules"
diff --git a/tests/PlatformCompatGating/AndroidManifest.xml b/tests/PlatformCompatGating/AndroidManifest.xml
index 7f14b83..c24dc31 100644
--- a/tests/PlatformCompatGating/AndroidManifest.xml
+++ b/tests/PlatformCompatGating/AndroidManifest.xml
@@ -6,6 +6,6 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.tests.gating"/>
 </manifest>
diff --git a/tests/PlatformCompatGating/AndroidTest.xml b/tests/PlatformCompatGating/AndroidTest.xml
index c626848..0c7485b 100644
--- a/tests/PlatformCompatGating/AndroidTest.xml
+++ b/tests/PlatformCompatGating/AndroidTest.xml
@@ -24,7 +24,6 @@
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest">
         <option name="package" value="com.android.tests.gating"/>
-        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
         <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>
diff --git a/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatGatingTest.java b/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatGatingTest.java
index dc317f19..c1ce0e9 100644
--- a/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatGatingTest.java
+++ b/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatGatingTest.java
@@ -18,8 +18,9 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.compat.testing.PlatformCompatChangeRule;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.compat.testing.DummyApi;
 
@@ -81,14 +82,14 @@
     @Test
     @EnableCompatChanges({DummyApi.CHANGE_SYSTEM_SERVER})
     public void testDummyGatingPositiveSystemServer() {
-        assertThat(
-                DummyApi.dummySystemServer(InstrumentationRegistry.getTargetContext())).isTrue();
+        assertThat(DummyApi.dummySystemServer(
+                InstrumentationRegistry.getInstrumentation().getTargetContext())).isTrue();
     }
 
     @Test
     @DisableCompatChanges({DummyApi.CHANGE_SYSTEM_SERVER})
     public void testDummyGatingNegativeSystemServer() {
-        assertThat(
-                DummyApi.dummySystemServer(InstrumentationRegistry.getTargetContext())).isFalse();
+        assertThat(DummyApi.dummySystemServer(
+                InstrumentationRegistry.getInstrumentation().getTargetContext())).isFalse();
     }
 }
diff --git a/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatPermissionsTest.java b/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatPermissionsTest.java
new file mode 100644
index 0000000..9b9e581
--- /dev/null
+++ b/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatPermissionsTest.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2020 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.tests.gating;
+
+import static android.Manifest.permission.LOG_COMPAT_CHANGE;
+import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG;
+import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG;
+
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.compat.Compatibility.ChangeConfig;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Process;
+import android.os.ServiceManager;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.compat.CompatibilityChangeConfig;
+import com.android.internal.compat.IPlatformCompat;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.HashSet;
+import java.util.Set;
+
+@RunWith(JUnit4.class)
+public final class PlatformCompatPermissionsTest {
+
+    // private Context mContext;
+    private IPlatformCompat mPlatformCompat;
+
+    @Rule
+    public final ExpectedException thrown = ExpectedException.none();
+    private Context mContext;
+    private UiAutomation mUiAutomation;
+    private PackageManager mPackageManager;
+
+    @Before
+    public void setUp() {
+        // mContext;
+        mPlatformCompat = IPlatformCompat.Stub
+            .asInterface(ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        mUiAutomation = instrumentation.getUiAutomation();
+        mContext = instrumentation.getTargetContext();
+
+        mPackageManager = mContext.getPackageManager();
+    }
+
+    @After
+    public void tearDown() {
+
+        mUiAutomation.dropShellPermissionIdentity();
+    }
+
+    @Test
+    public void reportChange_noLogCompatChangePermission_throwsSecurityException()
+            throws Throwable {
+        thrown.expect(SecurityException.class);
+        final String packageName = mContext.getPackageName();
+
+        mPlatformCompat.reportChange(1, mPackageManager.getApplicationInfo(packageName, 0));
+    }
+
+    @Test
+    public void reportChange_logCompatChangePermission_noThrow()
+            throws Throwable {
+        mUiAutomation.adoptShellPermissionIdentity(LOG_COMPAT_CHANGE);
+        final String packageName = mContext.getPackageName();
+
+        mPlatformCompat.reportChange(1, mPackageManager.getApplicationInfo(packageName, 0));
+    }
+
+    @Test
+    public void reportChangeByPackageName_noLogCompatChangePermission_throwsSecurityException()
+            throws Throwable {
+        thrown.expect(SecurityException.class);
+        final String packageName = mContext.getPackageName();
+
+        mPlatformCompat.reportChangeByPackageName(1, packageName, 0);
+    }
+
+    @Test
+    public void reportChangeByPackageName_logCompatChangePermission_noThrow()
+            throws Throwable {
+        mUiAutomation.adoptShellPermissionIdentity(LOG_COMPAT_CHANGE);
+        final String packageName = mContext.getPackageName();
+
+        mPlatformCompat.reportChangeByPackageName(1, packageName, 0);
+    }
+
+    @Test
+    public void reportChangeByUid_noLogCompatChangePermission_throwsSecurityException()
+            throws Throwable {
+        thrown.expect(SecurityException.class);
+
+        mPlatformCompat.reportChangeByUid(1, Process.myUid());
+    }
+
+    @Test
+    public void reportChangeByUid_logCompatChangePermission_noThrow()
+            throws Throwable {
+        mUiAutomation.adoptShellPermissionIdentity(LOG_COMPAT_CHANGE);
+
+        mPlatformCompat.reportChangeByUid(1, Process.myUid());
+    }
+
+    @Test
+    public void isChangeEnabled_noReadCompatConfigPermission_throwsSecurityException()
+            throws Throwable {
+        thrown.expect(SecurityException.class);
+        final String packageName = mContext.getPackageName();
+
+        mPlatformCompat.isChangeEnabled(1, mPackageManager.getApplicationInfo(packageName, 0));
+    }
+
+    @Test
+    public void isChangeEnabled_noLogCompatChangeConfigPermission_throwsSecurityException()
+            throws Throwable {
+        thrown.expect(SecurityException.class);
+        mUiAutomation.adoptShellPermissionIdentity(READ_COMPAT_CHANGE_CONFIG);
+        final String packageName = mContext.getPackageName();
+
+        mPlatformCompat.isChangeEnabled(1, mPackageManager.getApplicationInfo(packageName, 0));
+    }
+
+    @Test
+    public void isChangeEnabled_readAndLogCompatChangeConfigPermission_noThrow()
+            throws Throwable {
+        mUiAutomation.adoptShellPermissionIdentity(READ_COMPAT_CHANGE_CONFIG, LOG_COMPAT_CHANGE);
+        final String packageName = mContext.getPackageName();
+
+        mPlatformCompat.isChangeEnabled(1, mPackageManager.getApplicationInfo(packageName, 0));
+    }
+
+    @Test
+    public void isChangeEnabledByPackageName_noReadCompatConfigPermission_throwsSecurityException()
+            throws Throwable {
+        thrown.expect(SecurityException.class);
+        final String packageName = mContext.getPackageName();
+
+        mPlatformCompat.isChangeEnabledByPackageName(1, packageName, 0);
+    }
+
+    @Test
+    public void isChangeEnabledByPackageName_noLogompatConfigPermission_throwsSecurityException()
+            throws Throwable {
+        thrown.expect(SecurityException.class);
+        mUiAutomation.adoptShellPermissionIdentity(READ_COMPAT_CHANGE_CONFIG);
+        final String packageName = mContext.getPackageName();
+
+        mPlatformCompat.isChangeEnabledByPackageName(1, packageName, 0);
+    }
+
+    @Test
+    public void isChangeEnabledByPackageName_readAndLogCompatChangeConfigPermission_noThrow()
+            throws Throwable {
+        mUiAutomation.adoptShellPermissionIdentity(READ_COMPAT_CHANGE_CONFIG, LOG_COMPAT_CHANGE);
+        final String packageName = mContext.getPackageName();
+
+        mPlatformCompat.isChangeEnabledByPackageName(1, packageName, 0);
+    }
+
+    @Test
+    public void isChangeEnabledByUid_noReadCompatConfigPermission_throwsSecurityException()
+            throws Throwable {
+        thrown.expect(SecurityException.class);
+
+        mPlatformCompat.isChangeEnabledByUid(1, Process.myUid());
+    }
+
+    @Test
+    public void isChangeEnabledByUid_noLogCompatChangePermission_throwsSecurityException()
+            throws Throwable {
+        thrown.expect(SecurityException.class);
+        mUiAutomation.adoptShellPermissionIdentity(READ_COMPAT_CHANGE_CONFIG);
+
+        mPlatformCompat.isChangeEnabledByUid(1, Process.myUid());
+    }
+
+    @Test
+    public void isChangeEnabledByUid_readAndLogCompatChangeConfigPermission_noThrow()
+            throws Throwable {
+        mUiAutomation.adoptShellPermissionIdentity(READ_COMPAT_CHANGE_CONFIG, LOG_COMPAT_CHANGE);
+
+        mPlatformCompat.isChangeEnabledByUid(1, Process.myUid());
+    }
+
+    @Test
+    public void setOverrides_noOverridesPermission_throwsSecurityException()
+            throws Throwable {
+        thrown.expect(SecurityException.class);
+        Set<Long> enabled = new HashSet<>();
+        Set<Long> disabled = new HashSet<>();
+        ChangeConfig changeConfig = new ChangeConfig(enabled, disabled);
+        CompatibilityChangeConfig compatibilityChangeConfig =
+                new CompatibilityChangeConfig(changeConfig);
+
+        mPlatformCompat.setOverrides(compatibilityChangeConfig, "foo.bar");
+    }
+    @Test
+    public void setOverrides_overridesPermission_noThrow()
+            throws Throwable {
+        mUiAutomation.adoptShellPermissionIdentity(OVERRIDE_COMPAT_CHANGE_CONFIG);
+        Set<Long> enabled = new HashSet<>();
+        Set<Long> disabled = new HashSet<>();
+        ChangeConfig changeConfig = new ChangeConfig(enabled, disabled);
+        CompatibilityChangeConfig compatibilityChangeConfig =
+                new CompatibilityChangeConfig(changeConfig);
+
+        mPlatformCompat.setOverrides(compatibilityChangeConfig, "foo.bar");
+    }
+
+    @Test
+    public void setOverridesForTest_noOverridesPermission_throwsSecurityException()
+            throws Throwable {
+        thrown.expect(SecurityException.class);
+        Set<Long> enabled = new HashSet<>();
+        Set<Long> disabled = new HashSet<>();
+        ChangeConfig changeConfig = new ChangeConfig(enabled, disabled);
+        CompatibilityChangeConfig compatibilityChangeConfig =
+                new CompatibilityChangeConfig(changeConfig);
+
+        mPlatformCompat.setOverridesForTest(compatibilityChangeConfig, "foo.bar");
+    }
+    @Test
+    public void setOverridesForTest_overridesPermission_noThrow()
+            throws Throwable {
+        mUiAutomation.adoptShellPermissionIdentity(OVERRIDE_COMPAT_CHANGE_CONFIG);
+        Set<Long> enabled = new HashSet<>();
+        Set<Long> disabled = new HashSet<>();
+        ChangeConfig changeConfig = new ChangeConfig(enabled, disabled);
+        CompatibilityChangeConfig compatibilityChangeConfig =
+                new CompatibilityChangeConfig(changeConfig);
+
+        mPlatformCompat.setOverridesForTest(compatibilityChangeConfig, "foo.bar");
+    }
+
+    @Test
+    public void clearOverrides_noOverridesPermission_throwsSecurityException()
+            throws Throwable {
+        thrown.expect(SecurityException.class);
+
+        mPlatformCompat.clearOverrides("foo.bar");
+    }
+    @Test
+    public void clearOverrides_overridesPermission_noThrow()
+            throws Throwable {
+        mUiAutomation.adoptShellPermissionIdentity(OVERRIDE_COMPAT_CHANGE_CONFIG);
+
+        mPlatformCompat.clearOverrides("foo.bar");
+    }
+
+    @Test
+    public void clearOverridesForTest_noOverridesPermission_throwsSecurityException()
+            throws Throwable {
+        thrown.expect(SecurityException.class);
+
+        mPlatformCompat.clearOverridesForTest("foo.bar");
+    }
+    @Test
+    public void clearOverridesForTest_overridesPermission_noThrow()
+            throws Throwable {
+        mUiAutomation.adoptShellPermissionIdentity(OVERRIDE_COMPAT_CHANGE_CONFIG);
+
+        mPlatformCompat.clearOverridesForTest("foo.bar");
+    }
+
+    @Test
+    public void clearOverride_noOverridesPermission_throwsSecurityException()
+            throws Throwable {
+        thrown.expect(SecurityException.class);
+
+        mPlatformCompat.clearOverride(1, "foo.bar");
+    }
+    @Test
+    public void clearOverride_overridesPermission_noThrow()
+            throws Throwable {
+        mUiAutomation.adoptShellPermissionIdentity(OVERRIDE_COMPAT_CHANGE_CONFIG);
+
+        mPlatformCompat.clearOverride(1, "foo.bar");
+    }
+
+    @Test
+    public void listAllChanges_noReadCompatConfigPermission_throwsSecurityException()
+            throws Throwable {
+        thrown.expect(SecurityException.class);
+
+        mPlatformCompat.listAllChanges();
+    }
+    @Test
+    public void listAllChanges_readCompatConfigPermission_noThrow()
+            throws Throwable {
+        mUiAutomation.adoptShellPermissionIdentity(READ_COMPAT_CHANGE_CONFIG);
+
+        mPlatformCompat.listAllChanges();
+    }
+}
diff --git a/tests/PlatformCompatGating/test-rules/Android.bp b/tests/PlatformCompatGating/test-rules/Android.bp
index 8211ef5..10fa2dc 100644
--- a/tests/PlatformCompatGating/test-rules/Android.bp
+++ b/tests/PlatformCompatGating/test-rules/Android.bp
@@ -19,7 +19,7 @@
     srcs: ["src/**/*.java"],
     static_libs: [
         "junit",
-        "android-support-test",
+        "androidx.test.core",
         "truth-prebuilt",
         "core-compat-test-rules"
     ],
diff --git a/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java b/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java
index 932ec64..d6846fa 100644
--- a/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java
+++ b/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java
@@ -16,13 +16,17 @@
 
 package android.compat.testing;
 
+import android.Manifest;
 import android.app.Instrumentation;
+import android.app.UiAutomation;
 import android.compat.Compatibility;
 import android.compat.Compatibility.ChangeConfig;
 import android.content.Context;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.support.test.InstrumentationRegistry;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
 
 import com.android.internal.compat.CompatibilityChangeConfig;
 import com.android.internal.compat.IPlatformCompat;
@@ -83,12 +87,17 @@
         @Override
         public void evaluate() throws Throwable {
             Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+            UiAutomation uiAutomation = instrumentation.getUiAutomation();
             String packageName = instrumentation.getTargetContext().getPackageName();
             IPlatformCompat platformCompat = IPlatformCompat.Stub
                     .asInterface(ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
             if (platformCompat == null) {
                 throw new IllegalStateException("Could not get IPlatformCompat service!");
             }
+            uiAutomation.adoptShellPermissionIdentity(
+                    Manifest.permission.LOG_COMPAT_CHANGE,
+                    Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG,
+                    Manifest.permission.READ_COMPAT_CHANGE_CONFIG);
             Compatibility.setOverrides(mConfig);
             try {
                 platformCompat.setOverridesForTest(new CompatibilityChangeConfig(mConfig),
@@ -101,6 +110,7 @@
             } catch (RemoteException e) {
                 throw new RuntimeException("Could not call IPlatformCompat binder method!", e);
             } finally {
+                uiAutomation.dropShellPermissionIdentity();
                 Compatibility.clearOverrides();
             }
         }
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index b4f32e7..a0e98d0 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -6446,14 +6446,16 @@
     public void testRegisterUnregisterConnectivityDiagnosticsCallback() throws Exception {
         final NetworkRequest wifiRequest =
                 new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI).build();
-
         when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
 
         mService.registerConnectivityDiagnosticsCallback(
                 mConnectivityDiagnosticsCallback, wifiRequest, mContext.getPackageName());
 
-        verify(mIBinder, timeout(TIMEOUT_MS))
-                .linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt());
+        // Block until all other events are done processing.
+        HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+
+        verify(mIBinder).linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt());
+        verify(mConnectivityDiagnosticsCallback).asBinder();
         assertTrue(
                 mService.mConnectivityDiagnosticsCallbacks.containsKey(
                         mConnectivityDiagnosticsCallback));
@@ -6476,8 +6478,10 @@
         mService.registerConnectivityDiagnosticsCallback(
                 mConnectivityDiagnosticsCallback, wifiRequest, mContext.getPackageName());
 
-        verify(mIBinder, timeout(TIMEOUT_MS))
-                .linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt());
+        // Block until all other events are done processing.
+        HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+
+        verify(mIBinder).linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt());
         verify(mConnectivityDiagnosticsCallback).asBinder();
         assertTrue(
                 mService.mConnectivityDiagnosticsCallbacks.containsKey(
@@ -6635,8 +6639,11 @@
     public void testConnectivityDiagnosticsCallbackOnConnectivityReport() throws Exception {
         setUpConnectivityDiagnosticsCallback();
 
-        // Wait for onConnectivityReport to fire
-        verify(mConnectivityDiagnosticsCallback, timeout(TIMEOUT_MS))
+        // Block until all other events are done processing.
+        HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+
+        // Verify onConnectivityReport fired
+        verify(mConnectivityDiagnosticsCallback)
                 .onConnectivityReport(any(ConnectivityReport.class));
     }
 
@@ -6648,9 +6655,11 @@
         // cellular network agent
         mCellNetworkAgent.notifyDataStallSuspected();
 
-        // Wait for onDataStallSuspected to fire
-        verify(mConnectivityDiagnosticsCallback, timeout(TIMEOUT_MS))
-                .onDataStallSuspected(any(DataStallReport.class));
+        // Block until all other events are done processing.
+        HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+
+        // Verify onDataStallSuspected fired
+        verify(mConnectivityDiagnosticsCallback).onDataStallSuspected(any(DataStallReport.class));
     }
 
     @Test
@@ -6661,15 +6670,21 @@
         final boolean hasConnectivity = true;
         mService.reportNetworkConnectivity(n, hasConnectivity);
 
-        // Wait for onNetworkConnectivityReported to fire
-        verify(mConnectivityDiagnosticsCallback, timeout(TIMEOUT_MS))
+        // Block until all other events are done processing.
+        HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+
+        // Verify onNetworkConnectivityReported fired
+        verify(mConnectivityDiagnosticsCallback)
                 .onNetworkConnectivityReported(eq(n), eq(hasConnectivity));
 
         final boolean noConnectivity = false;
         mService.reportNetworkConnectivity(n, noConnectivity);
 
+        // Block until all other events are done processing.
+        HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+
         // Wait for onNetworkConnectivityReported to fire
-        verify(mConnectivityDiagnosticsCallback, timeout(TIMEOUT_MS))
+        verify(mConnectivityDiagnosticsCallback)
                 .onNetworkConnectivityReported(eq(n), eq(noConnectivity));
     }
 }
diff --git a/wifi/Android.bp b/wifi/Android.bp
index dae04c6..6a29b1c 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -63,7 +63,6 @@
     "//frameworks/base/wifi/tests",
     "//frameworks/opt/net/wifi/tests/wifitests:__subpackages__",
 
-    "//frameworks/opt/net/wifi/libs/WifiTrackerLib/tests",
     "//external/robolectric-shadows:__subpackages__",
     "//frameworks/base/packages/SettingsLib/tests/integ",
     "//external/sl4a:__subpackages__",