Merge "Hold a strong reference to the callback in TextClassifierService"
diff --git a/api/current.txt b/api/current.txt
index 652f23f..c68120d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4278,6 +4278,7 @@
     method @Deprecated public int checkOpNoThrow(@NonNull String, int, @NonNull String);
     method public void checkPackage(int, @NonNull String);
     method public void finishOp(@NonNull String, int, @NonNull String);
+    method public boolean isOpActive(@NonNull String, int, @NonNull String);
     method public int noteOp(@NonNull String, int, @NonNull String);
     method public int noteOpNoThrow(@NonNull String, int, @NonNull String);
     method public int noteProxyOp(@NonNull String, @NonNull String);
@@ -4286,8 +4287,10 @@
     method public static String permissionToOp(String);
     method public int startOp(@NonNull String, int, @NonNull String);
     method public int startOpNoThrow(@NonNull String, int, @NonNull String);
+    method public void startWatchingActive(@NonNull String[], @NonNull java.util.concurrent.Executor, @NonNull android.app.AppOpsManager.OnOpActiveChangedListener);
     method public void startWatchingMode(@NonNull String, @Nullable String, @NonNull android.app.AppOpsManager.OnOpChangedListener);
     method public void startWatchingMode(@NonNull String, @Nullable String, int, @NonNull android.app.AppOpsManager.OnOpChangedListener);
+    method public void stopWatchingActive(@NonNull android.app.AppOpsManager.OnOpActiveChangedListener);
     method public void stopWatchingMode(@NonNull android.app.AppOpsManager.OnOpChangedListener);
     method public int unsafeCheckOp(@NonNull String, int, @NonNull String);
     method public int unsafeCheckOpNoThrow(@NonNull String, int, @NonNull String);
@@ -4335,6 +4338,10 @@
     field public static final int WATCH_FOREGROUND_CHANGES = 1; // 0x1
   }
 
+  public static interface AppOpsManager.OnOpActiveChangedListener {
+    method public void onOpActiveChanged(@NonNull String, int, @NonNull String, boolean);
+  }
+
   public static interface AppOpsManager.OnOpChangedListener {
     method public void onOpChanged(String, String);
   }
diff --git a/api/system-current.txt b/api/system-current.txt
index ed2616a..86e5f2e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -353,6 +353,9 @@
     field public static final String OPSTR_PROJECT_MEDIA = "android:project_media";
     field public static final String OPSTR_READ_CLIPBOARD = "android:read_clipboard";
     field public static final String OPSTR_READ_ICC_SMS = "android:read_icc_sms";
+    field public static final String OPSTR_READ_MEDIA_AUDIO = "android:read_media_audio";
+    field public static final String OPSTR_READ_MEDIA_IMAGES = "android:read_media_images";
+    field public static final String OPSTR_READ_MEDIA_VIDEO = "android:read_media_video";
     field public static final String OPSTR_RECEIVE_EMERGENCY_BROADCAST = "android:receive_emergency_broadcast";
     field public static final String OPSTR_REQUEST_DELETE_PACKAGES = "android:request_delete_packages";
     field public static final String OPSTR_REQUEST_INSTALL_PACKAGES = "android:request_install_packages";
@@ -368,6 +371,9 @@
     field public static final String OPSTR_WIFI_SCAN = "android:wifi_scan";
     field public static final String OPSTR_WRITE_CLIPBOARD = "android:write_clipboard";
     field public static final String OPSTR_WRITE_ICC_SMS = "android:write_icc_sms";
+    field public static final String OPSTR_WRITE_MEDIA_AUDIO = "android:write_media_audio";
+    field public static final String OPSTR_WRITE_MEDIA_IMAGES = "android:write_media_images";
+    field public static final String OPSTR_WRITE_MEDIA_VIDEO = "android:write_media_video";
     field public static final String OPSTR_WRITE_SMS = "android:write_sms";
     field public static final String OPSTR_WRITE_WALLPAPER = "android:write_wallpaper";
     field public static final int OP_FLAGS_ALL = 31; // 0x1f
diff --git a/api/test-current.txt b/api/test-current.txt
index 1b2c59d..ec3cb06 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -160,8 +160,6 @@
     method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(int, int, String, int);
     method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(String, int, String, int);
     method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setUidMode(String, int, int);
-    method public void startWatchingActive(@NonNull int[], @NonNull android.app.AppOpsManager.OnOpActiveChangedListener);
-    method public void stopWatchingActive(@NonNull android.app.AppOpsManager.OnOpActiveChangedListener);
     method public static int strOpToOp(@NonNull String);
     field public static final int HISTORICAL_MODE_DISABLED = 0; // 0x0
     field public static final int HISTORICAL_MODE_ENABLED_ACTIVE = 1; // 0x1
@@ -193,6 +191,9 @@
     field public static final String OPSTR_PROJECT_MEDIA = "android:project_media";
     field public static final String OPSTR_READ_CLIPBOARD = "android:read_clipboard";
     field public static final String OPSTR_READ_ICC_SMS = "android:read_icc_sms";
+    field public static final String OPSTR_READ_MEDIA_AUDIO = "android:read_media_audio";
+    field public static final String OPSTR_READ_MEDIA_IMAGES = "android:read_media_images";
+    field public static final String OPSTR_READ_MEDIA_VIDEO = "android:read_media_video";
     field public static final String OPSTR_RECEIVE_EMERGENCY_BROADCAST = "android:receive_emergency_broadcast";
     field public static final String OPSTR_REQUEST_DELETE_PACKAGES = "android:request_delete_packages";
     field public static final String OPSTR_REQUEST_INSTALL_PACKAGES = "android:request_install_packages";
@@ -208,6 +209,9 @@
     field public static final String OPSTR_WIFI_SCAN = "android:wifi_scan";
     field public static final String OPSTR_WRITE_CLIPBOARD = "android:write_clipboard";
     field public static final String OPSTR_WRITE_ICC_SMS = "android:write_icc_sms";
+    field public static final String OPSTR_WRITE_MEDIA_AUDIO = "android:write_media_audio";
+    field public static final String OPSTR_WRITE_MEDIA_IMAGES = "android:write_media_images";
+    field public static final String OPSTR_WRITE_MEDIA_VIDEO = "android:write_media_video";
     field public static final String OPSTR_WRITE_SMS = "android:write_sms";
     field public static final String OPSTR_WRITE_WALLPAPER = "android:write_wallpaper";
     field public static final int OP_COARSE_LOCATION = 0; // 0x0
@@ -293,10 +297,6 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalUidOps> CREATOR;
   }
 
-  public static interface AppOpsManager.OnOpActiveChangedListener {
-    method public void onOpActiveChanged(int, int, String, boolean);
-  }
-
   public static final class AppOpsManager.OpEntry implements android.os.Parcelable {
     method public int describeContents();
     method public long getDuration();
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index b875470..d9c04f2 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -66,13 +66,13 @@
 message CountMetricData {
   optional DimensionsValue dimensions_in_what = 1;
 
-  optional DimensionsValue dimensions_in_condition = 2;
+  optional DimensionsValue dimensions_in_condition = 2 [deprecated = true];
 
   repeated CountBucketInfo bucket_info = 3;
 
   repeated DimensionsValue dimension_leaf_values_in_what = 4;
 
-  repeated DimensionsValue dimension_leaf_values_in_condition = 5;
+  repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true];
 }
 
 message DurationBucketInfo {
@@ -92,13 +92,13 @@
 message DurationMetricData {
   optional DimensionsValue dimensions_in_what = 1;
 
-  optional DimensionsValue dimensions_in_condition = 2;
+  optional DimensionsValue dimensions_in_condition = 2 [deprecated = true];
 
   repeated DurationBucketInfo bucket_info = 3;
 
   repeated DimensionsValue dimension_leaf_values_in_what = 4;
 
-  repeated DimensionsValue dimension_leaf_values_in_condition = 5;
+  repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true];
 }
 
 message ValueBucketInfo {
@@ -136,13 +136,13 @@
 message ValueMetricData {
   optional DimensionsValue dimensions_in_what = 1;
 
-  optional DimensionsValue dimensions_in_condition = 2;
+  optional DimensionsValue dimensions_in_condition = 2 [deprecated = true];
 
   repeated ValueBucketInfo bucket_info = 3;
 
   repeated DimensionsValue dimension_leaf_values_in_what = 4;
 
-  repeated DimensionsValue dimension_leaf_values_in_condition = 5;
+  repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true];
 }
 
 message GaugeBucketInfo {
@@ -166,13 +166,13 @@
 message GaugeMetricData {
   optional DimensionsValue dimensions_in_what = 1;
 
-  optional DimensionsValue dimensions_in_condition = 2;
+  optional DimensionsValue dimensions_in_condition = 2 [deprecated = true];
 
   repeated GaugeBucketInfo bucket_info = 3;
 
   repeated DimensionsValue dimension_leaf_values_in_what = 4;
 
-  repeated DimensionsValue dimension_leaf_values_in_condition = 5;
+  repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true];
 }
 
 message StatsLogReport {
@@ -220,7 +220,7 @@
 
   optional DimensionsValue dimensions_path_in_what = 11;
 
-  optional DimensionsValue dimensions_path_in_condition = 12;
+  optional DimensionsValue dimensions_path_in_condition = 12 [deprecated = true];
 
   // DO NOT USE field 13.
 
@@ -363,7 +363,7 @@
         repeated ConditionStats condition_stats = 14;
         repeated MetricStats metric_stats = 15;
         repeated AlertStats alert_stats = 16;
-        repeated MetricStats metric_dimension_in_condition_stats = 17;
+        repeated MetricStats metric_dimension_in_condition_stats = 17 [deprecated = true];
         message Annotation {
             optional int64 field_int64 = 1;
             optional int32 field_int32 = 2;
@@ -483,7 +483,7 @@
     message MetricValue {
         optional int64 metric_id = 1;
         optional DimensionsValue dimension_in_what = 2;
-        optional DimensionsValue dimension_in_condition = 3;
+        optional DimensionsValue dimension_in_condition = 3 [deprecated = true];
         optional int64 value = 4;
     }
     oneof value {
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 79c06b9..1b7f398 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -182,7 +182,7 @@
 
   optional FieldMatcher dimensions_in_what = 4;
 
-  optional FieldMatcher dimensions_in_condition = 7;
+  optional FieldMatcher dimensions_in_condition = 7 [deprecated = true];
 
   optional TimeUnit bucket = 5;
 
@@ -207,7 +207,7 @@
 
   optional FieldMatcher dimensions_in_what = 6;
 
-  optional FieldMatcher dimensions_in_condition = 8;
+  optional FieldMatcher dimensions_in_condition = 8 [deprecated = true];
 
   optional TimeUnit bucket = 7;
 }
@@ -225,7 +225,7 @@
 
   optional FieldMatcher dimensions_in_what = 5;
 
-  optional FieldMatcher dimensions_in_condition = 8;
+  optional FieldMatcher dimensions_in_condition = 8 [deprecated = true];
 
   optional TimeUnit bucket = 6;
 
@@ -259,7 +259,7 @@
 
   optional FieldMatcher dimensions_in_what = 5;
 
-  optional FieldMatcher dimensions_in_condition = 9;
+  optional FieldMatcher dimensions_in_condition = 9 [deprecated = true];
 
   optional TimeUnit bucket = 6;
 
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index d20cc41..34045c9 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import android.Manifest;
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -29,6 +30,7 @@
 import android.app.usage.UsageStatsManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ParceledListSlice;
 import android.media.AudioAttributes.AttributeUsage;
 import android.os.Binder;
@@ -1095,21 +1097,27 @@
             "android:sms_financial_transactions";
 
     /** @hide Read media of audio type. */
+    @SystemApi @TestApi
     public static final String OPSTR_READ_MEDIA_AUDIO = "android:read_media_audio";
     /** @hide Write media of audio type. */
+    @SystemApi @TestApi
     public static final String OPSTR_WRITE_MEDIA_AUDIO = "android:write_media_audio";
     /** @hide Read media of video type. */
+    @SystemApi @TestApi
     public static final String OPSTR_READ_MEDIA_VIDEO = "android:read_media_video";
     /** @hide Write media of video type. */
+    @SystemApi @TestApi
     public static final String OPSTR_WRITE_MEDIA_VIDEO = "android:write_media_video";
     /** @hide Read media of image type. */
+    @SystemApi @TestApi
     public static final String OPSTR_READ_MEDIA_IMAGES = "android:read_media_images";
     /** @hide Write media of image type. */
+    @SystemApi @TestApi
     public static final String OPSTR_WRITE_MEDIA_IMAGES = "android:write_media_images";
     /** @hide Has a legacy (non-isolated) view of storage. */
-    @TestApi
-    @SystemApi
+    @SystemApi @TestApi
     public static final String OPSTR_LEGACY_STORAGE = "android:legacy_storage";
+
     /** @hide Interact with accessibility. */
     @SystemApi
     public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility";
@@ -4281,20 +4289,17 @@
 
     /**
      * Callback for notification of changes to operation active state.
-     *
-     * @hide
      */
-    @TestApi
     public interface OnOpActiveChangedListener {
         /**
          * Called when the active state of an app op changes.
          *
-         * @param code The op code.
-         * @param uid The UID performing the operation.
+         * @param op The operation that changed.
          * @param packageName The package performing the operation.
          * @param active Whether the operation became active or inactive.
          */
-        void onOpActiveChanged(int code, int uid, String packageName, boolean active);
+        void onOpActiveChanged(@NonNull String op, int uid, @NonNull String packageName,
+                boolean active);
     }
 
     /**
@@ -4324,6 +4329,16 @@
         public void onOpChanged(int op, String packageName) { }
     }
 
+    /**
+     * Callback for notification of changes to operation state.
+     * This allows you to see the raw op codes instead of strings.
+     * @hide
+     */
+    public interface OnOpActiveChangedInternalListener extends OnOpActiveChangedListener {
+        default void onOpActiveChanged(String op, int uid, String packageName, boolean active) { }
+        default void onOpActiveChanged(int op, int uid, String packageName, boolean active) { }
+    }
+
     AppOpsManager(Context context, IAppOpsService service) {
         mContext = context;
         mService = service;
@@ -4779,6 +4794,17 @@
         }
     }
 
+    /** {@hide} */
+    @Deprecated
+    public void startWatchingActive(@NonNull int[] ops,
+            @NonNull OnOpActiveChangedListener callback) {
+        final String[] strOps = new String[ops.length];
+        for (int i = 0; i < ops.length; i++) {
+            strOps[i] = opToPublicName(ops[i]);
+        }
+        startWatchingActive(strOps, mContext.getMainExecutor(), callback);
+    }
+
     /**
      * Start watching for changes to the active state of app ops. An app op may be
      * long running and it has a clear start and stop delimiters. If an op is being
@@ -4786,26 +4812,25 @@
      * watched ops for a registered callback you need to unregister and register it
      * again.
      *
-     * <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission
+     * <p> If you don't hold the {@code android.Manifest.permission#WATCH_APPOPS} permission
      * you can watch changes only for your UID.
      *
-     * @param ops The ops to watch.
+     * @param ops The operations to watch.
      * @param callback Where to report changes.
      *
-     * @see #isOperationActive(int, int, String)
-     * @see #stopWatchingActive(OnOpActiveChangedListener)
+     * @see #isOperationActive
+     * @see #stopWatchingActive
      * @see #startOp(int, int, String)
      * @see #finishOp(int, int, String)
-     *
-     * @hide
      */
-    @TestApi
     // TODO: Uncomment below annotation once b/73559440 is fixed
     // @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
-    public void startWatchingActive(@NonNull int[] ops,
+    public void startWatchingActive(@NonNull String[] ops,
+            @CallbackExecutor @NonNull Executor executor,
             @NonNull OnOpActiveChangedListener callback) {
-        Preconditions.checkNotNull(ops, "ops cannot be null");
-        Preconditions.checkNotNull(callback, "callback cannot be null");
+        Objects.requireNonNull(ops);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
         IAppOpsActiveCallback cb;
         synchronized (mActiveWatchers) {
             cb = mActiveWatchers.get(callback);
@@ -4815,13 +4840,25 @@
             cb = new IAppOpsActiveCallback.Stub() {
                 @Override
                 public void opActiveChanged(int op, int uid, String packageName, boolean active) {
-                    callback.onOpActiveChanged(op, uid, packageName, active);
+                    executor.execute(() -> {
+                        if (callback instanceof OnOpActiveChangedInternalListener) {
+                            ((OnOpActiveChangedInternalListener) callback).onOpActiveChanged(op,
+                                    uid, packageName, active);
+                        }
+                        if (sOpToString[op] != null) {
+                            callback.onOpActiveChanged(sOpToString[op], uid, packageName, active);
+                        }
+                    });
                 }
             };
             mActiveWatchers.put(callback, cb);
         }
+        final int[] rawOps = new int[ops.length];
+        for (int i = 0; i < ops.length; i++) {
+            rawOps[i] = strOpToOp(ops[i]);
+        }
         try {
-            mService.startWatchingActive(ops, cb);
+            mService.startWatchingActive(rawOps, cb);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4832,14 +4869,11 @@
      * long running and it has a clear start and stop delimiters. Unregistering a
      * non-registered callback has no effect.
      *
-     * @see #isOperationActive#(int, int, String)
-     * @see #startWatchingActive(int[], OnOpActiveChangedListener)
+     * @see #isOperationActive
+     * @see #startWatchingActive
      * @see #startOp(int, int, String)
      * @see #finishOp(int, int, String)
-     *
-     * @hide
      */
-    @TestApi
     public void stopWatchingActive(@NonNull OnOpActiveChangedListener callback) {
         synchronized (mActiveWatchers) {
             final IAppOpsActiveCallback cb = mActiveWatchers.remove(callback);
@@ -5449,6 +5483,19 @@
     }
 
     /**
+     * Checks whether the given op for a package is active.
+     * <p>
+     * If you don't hold the {@code android.Manifest.permission#WATCH_APPOPS}
+     * permission you can query only for your UID.
+     *
+     * @see #finishOp(int)
+     * @see #startOp(int)
+     */
+    public boolean isOpActive(@NonNull String op, int uid, @NonNull String packageName) {
+        return isOperationActive(strOpToOp(op), uid, packageName);
+    }
+
+    /**
      * Checks whether the given op for a UID and package is active.
      *
      * <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index c49ea09..8987b23 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -883,7 +883,7 @@
             }
         }
 
-        // /aepx/com.android.runtime/lib, /vendor/lib, /odm/lib and /product/lib
+        // /apex/com.android.art/lib, /vendor/lib, /odm/lib and /product/lib
         // are added to the native lib search paths of the classloader.
         // Note that this is done AFTER the classloader is
         // created by ApplicationLoaders.getDefault().getClassLoader(...). The
@@ -904,8 +904,8 @@
         // (linker namespace).
         List<String> extraLibPaths = new ArrayList<>(4);
         String abiSuffix = VMRuntime.getRuntime().is64Bit() ? "64" : "";
-        if (!defaultSearchPaths.contains("/apex/com.android.runtime/lib")) {
-            extraLibPaths.add("/apex/com.android.runtime/lib" + abiSuffix);
+        if (!defaultSearchPaths.contains("/apex/com.android.art/lib")) {
+            extraLibPaths.add("/apex/com.android.art/lib" + abiSuffix);
         }
         if (!defaultSearchPaths.contains("/vendor/lib")) {
             extraLibPaths.add("/vendor/lib" + abiSuffix);
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index a3fd915..c12940a 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -245,6 +245,9 @@
 // Copying (CC) garbage collector.
 static const char* kNoGenerationalCCRuntimeOption = "-Xgc:nogenerational_cc";
 
+// Phenotype property name for enabling profiling the boot class path.
+static const char* PROFILE_BOOT_CLASS_PATH = "profilebootclasspath";
+
 // Feature flag name for running the JIT in Zygote experiment, b/119800099.
 static const char* ENABLE_APEX_IMAGE = "enable_apex_image";
 // Flag to pass to the runtime when using the apex image.
@@ -690,6 +693,24 @@
     char jdwpProviderBuf[sizeof("-XjdwpProvider:") - 1 + PROPERTY_VALUE_MAX];
     char bootImageBuf[sizeof("-Ximage:") - 1 + PROPERTY_VALUE_MAX];
 
+    // Read if we are using the profile configuration, do this at the start since the last ART args
+    // take precedence.
+    property_get("dalvik.vm.profilebootclasspath", propBuf, "");
+    std::string profile_boot_class_path = propBuf;
+    // Empty means the property is unset and we should default to the phenotype property.
+    // The possible values are {"true", "false", ""}
+    if (profile_boot_class_path.empty()) {
+        profile_boot_class_path = server_configurable_flags::GetServerConfigurableFlag(
+                RUNTIME_NATIVE_BOOT_NAMESPACE,
+                PROFILE_BOOT_CLASS_PATH,
+                /*default_value=*/ "");
+    }
+    if (profile_boot_class_path == "true") {
+        addOption("-Xps-profile-boot-class-path");
+        addOption("-Xps-profile-aot-code");
+        addOption("-Xjitsaveprofilinginfo");
+    }
+
     std::string use_apex_image =
         server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE,
                                                              ENABLE_APEX_IMAGE,
@@ -807,13 +828,6 @@
     parseRuntimeOption("dalvik.vm.jittransitionweight",
                        jittransitionweightOptBuf,
                        "-Xjittransitionweight:");
-
-    property_get("dalvik.vm.profilebootimage", propBuf, "");
-    if (strcmp(propBuf, "true") == 0) {
-        addOption("-Xps-profile-boot-class-path");
-        addOption("-Xps-profile-aot-code");
-    }
-
     /*
      * Madvise related options.
      */
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index ea4b252..bb57805 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -80,9 +80,9 @@
     return true;
   }
 
-  // Jars from the runtime apex are allowed.
-  static const char* kRuntimeApexPrefix = "/apex/com.android.runtime/javalib/";
-  if (android::base::StartsWith(path, kRuntimeApexPrefix)
+  // Jars from the ART APEX are allowed.
+  static const char* kArtApexPrefix = "/apex/com.android.art/javalib/";
+  if (android::base::StartsWith(path, kArtApexPrefix)
       && android::base::EndsWith(path, kJarSuffix)) {
     return true;
   }
diff --git a/packages/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml b/packages/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml
index 7846be1..363d885 100644
--- a/packages/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml
@@ -17,5 +17,5 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="search_menu" msgid="1604061903696928905">"Definições de pesquisa"</string>
+    <string name="search_menu" msgid="1604061903696928905">"Pesquisar definições"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 13890e0..f1fc9f9 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -452,7 +452,7 @@
     <string name="cancel" msgid="6859253417269739139">"বাতিল"</string>
     <string name="okay" msgid="1997666393121016642">"ঠিক আছে"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="8287824809739581837">"চালু করুন"</string>
-    <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"\'বিরক্ত করবেন না\' মোড চালু করুন"</string>
+    <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"\'বিরক্ত করবে না\' মোড চালু করুন"</string>
     <string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"কখনও নয়"</string>
     <string name="zen_interruption_level_priority" msgid="2078370238113347720">"শুধুমাত্র অগ্রাধিকার"</string>
     <string name="zen_mode_and_condition" msgid="4927230238450354412">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 2734f4e..7874aeb 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -286,7 +286,7 @@
     <string name="wait_for_debugger_summary" msgid="1766918303462746804">"Esperar que se conecte el depurador para iniciar la aplicación"</string>
     <string name="debug_input_category" msgid="1811069939601180246">"Entrada"</string>
     <string name="debug_drawing_category" msgid="6755716469267367852">"Dibujo"</string>
-    <string name="debug_hw_drawing_category" msgid="6220174216912308658">"Representación acelerada mediante hardware"</string>
+    <string name="debug_hw_drawing_category" msgid="6220174216912308658">"Procesamiento acelerado mediante hardware"</string>
     <string name="media_category" msgid="4388305075496848353">"Multimedia"</string>
     <string name="debug_monitoring_category" msgid="7640508148375798343">"Supervisión"</string>
     <string name="strict_mode" msgid="1938795874357830695">"Modo estricto"</string>
diff --git a/packages/SettingsLib/res/values-hy/arrays.xml b/packages/SettingsLib/res/values-hy/arrays.xml
index 5cffafe..7368f1d 100644
--- a/packages/SettingsLib/res/values-hy/arrays.xml
+++ b/packages/SettingsLib/res/values-hy/arrays.xml
@@ -43,7 +43,7 @@
     <item msgid="8937994881315223448">"Միացված է <xliff:g id="NETWORK_NAME">%1$s</xliff:g>-ին"</item>
     <item msgid="1330262655415760617">"Անջատված"</item>
     <item msgid="7698638434317271902">"Անջատվում է <xliff:g id="NETWORK_NAME">%1$s</xliff:g>-ից…"</item>
-    <item msgid="197508606402264311">"Անջատած է"</item>
+    <item msgid="197508606402264311">"Անջատված է"</item>
     <item msgid="8578370891960825148">"Անհաջող"</item>
     <item msgid="5660739516542454527">"Արգելափակված"</item>
     <item msgid="1805837518286731242">"Վատ ցանցից ժամանակավոր խուսափում"</item>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index bf58740..a74b4ae 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -263,7 +263,7 @@
     <string name="debug_view_attributes" msgid="6485448367803310384">"Միացնել ցուցադրման հատկանիշների ստուգումը"</string>
     <string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Միշտ ակտիվացրած պահել բջջային տվյալները, նույնիսկ Wi‑Fi-ը միացրած ժամանակ (ցանցերի միջև արագ փոխարկման համար):"</string>
     <string name="tethering_hardware_offload_summary" msgid="7726082075333346982">"Օգտագործել սարքակազմի արագացման միացումը, եթե հասանելի է"</string>
-    <string name="adb_warning_title" msgid="6234463310896563253">"Թույլատրե՞լ USB-ի վրիպազերծումը:"</string>
+    <string name="adb_warning_title" msgid="6234463310896563253">"Թույլատրե՞լ USB վրիպազերծումը:"</string>
     <string name="adb_warning_message" msgid="7316799925425402244">"USB վրիպազերծումը միայն ծրագրավորման նպատակների համար է: Օգտագործեք այն ձեր համակարգչից տվյալները ձեր սարք պատճենելու համար, առանց ծանուցման ձեր սարքի վրա ծրագրեր տեղադրելու և տվյալների մատյանը ընթերցելու համար:"</string>
     <string name="adb_keys_warning_message" msgid="5659849457135841625">"Փակե՞լ USB-ի վրիպազերծման մուտքը` անջատելով այն բոլոր համակարգիչներից, որտեղ նախկինում թույլատրել էիք:"</string>
     <string name="dev_settings_warning_title" msgid="7244607768088540165">"Ընդունե՞լ ծրագրավորման կարգավորումներ:"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index cf442b7..57e690d 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -159,7 +159,7 @@
     <string name="tts_default_pitch_title" msgid="6135942113172488671">"ஒலித்திறன்"</string>
     <string name="tts_default_pitch_summary" msgid="1944885882882650009">"உருவாக்கப்படும் பேச்சின் டோன் பாதிக்கப்படும்"</string>
     <string name="tts_default_lang_title" msgid="8018087612299820556">"மொழி"</string>
-    <string name="tts_lang_use_system" msgid="2679252467416513208">"அமைப்பின் மொழியில்"</string>
+    <string name="tts_lang_use_system" msgid="2679252467416513208">"அமைப்பின் மொழியைப் பயன்படுத்தவும்"</string>
     <string name="tts_lang_not_selected" msgid="7395787019276734765">"மொழி தேர்ந்தெடுக்கப்படவில்லை"</string>
     <string name="tts_default_lang_summary" msgid="5219362163902707785">"பேசப்படும் உரைக்கு மொழி சார்ந்த குரலை அமைக்கிறது"</string>
     <string name="tts_play_example_title" msgid="7094780383253097230">"எடுத்துக்காட்டைக் கவனிக்கவும்"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 83636dc..eb6160a 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -143,7 +143,7 @@
     <string name="data_usage_ota" msgid="5377889154805560860">"సిస్టమ్ అప్‌డేట్‌లు"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB టీథరింగ్"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"పోర్టబుల్ హాట్‌స్పాట్"</string>
-    <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"బ్లూటూత్ టీథరింగ్"</string>
+    <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"బ్లూటూత్ టెథెరింగ్"</string>
     <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"టీథరింగ్"</string>
     <string name="tether_settings_title_all" msgid="8356136101061143841">"టీథరింగ్ &amp; పోర్టబుల్ హాట్‌స్పాట్"</string>
     <string name="managed_user_title" msgid="8109605045406748842">"అన్ని కార్యాలయ అనువర్తనాలు"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 4e2d6fa..5c06c80 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -201,7 +201,7 @@
     <string name="vpn_settings_not_available" msgid="956841430176985598">"Cài đặt VPN không khả dụng cho người dùng này"</string>
     <string name="tethering_settings_not_available" msgid="6765770438438291012">"Cài đặt chia sẻ kết nối không khả dụng cho người dùng này"</string>
     <string name="apn_settings_not_available" msgid="7873729032165324000">"Cài đặt tên điểm truy cập không khả dụng cho người dùng này"</string>
-    <string name="enable_adb" msgid="7982306934419797485">"Gỡ lỗi USB"</string>
+    <string name="enable_adb" msgid="7982306934419797485">"Gỡ lỗi qua USB"</string>
     <string name="enable_adb_summary" msgid="4881186971746056635">"Bật chế độ gỡ lỗi khi kết nối USB"</string>
     <string name="clear_adb_keys" msgid="4038889221503122743">"Thu hồi ủy quyền gỡ lỗi USB"</string>
     <string name="bugreport_in_power" msgid="7923901846375587241">"Phím tắt báo cáo lỗi"</string>
@@ -263,7 +263,7 @@
     <string name="debug_view_attributes" msgid="6485448367803310384">"Cho phép kiểm tra thuộc tính của chế độ xem"</string>
     <string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Luôn bật dữ liệu di động ngay cả khi Wi-Fi đang hoạt động (để chuyển đổi mạng nhanh)."</string>
     <string name="tethering_hardware_offload_summary" msgid="7726082075333346982">"Sử dụng tính năng tăng tốc phần cứng khi chia sẻ kết nối nếu có"</string>
-    <string name="adb_warning_title" msgid="6234463310896563253">"Cho phép gỡ lỗi USB?"</string>
+    <string name="adb_warning_title" msgid="6234463310896563253">"Cho phép gỡ lỗi qua USB?"</string>
     <string name="adb_warning_message" msgid="7316799925425402244">"Gỡ lỗi USB chỉ dành cho mục đích phát triển. Hãy sử dụng tính năng này để sao chép dữ liệu giữa máy tính và thiết bị của bạn, cài đặt ứng dụng trên thiết bị của bạn mà không thông báo và đọc dữ liệu nhật ký."</string>
     <string name="adb_keys_warning_message" msgid="5659849457135841625">"Thu hồi quyền truy cập gỡ lỗi USB từ tất cả máy tính mà bạn đã ủy quyền trước đó?"</string>
     <string name="dev_settings_warning_title" msgid="7244607768088540165">"Cho phép cài đặt phát triển?"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index e85199f..c641302 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -329,7 +329,7 @@
     <string name="show_all_anrs" msgid="4924885492787069007">"顯示背景 ANR"</string>
     <string name="show_all_anrs_summary" msgid="6636514318275139826">"為背景應用程式顯示「應用程式無回應」對話方塊"</string>
     <string name="show_notification_channel_warnings" msgid="1399948193466922683">"顯示通知管道警告"</string>
-    <string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"當應用程式未經有效管道發佈通知時,在畫面上顯示警告"</string>
+    <string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"當應用程式未經有效管道發布通知時,在畫面上顯示警告"</string>
     <string name="force_allow_on_external" msgid="3215759785081916381">"強制允許將應用程式寫入外部儲存空間"</string>
     <string name="force_allow_on_external_summary" msgid="3640752408258034689">"允許將任何應用程式寫入外部儲存空間 (無論資訊清單值為何)"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"將活動強制設為可調整大小"</string>
diff --git a/packages/SoundPicker/res/layout-watch/radio_with_work_badge.xml b/packages/SoundPicker/res/layout-watch/radio_with_work_badge.xml
index 0e11621..ee29a37 100644
--- a/packages/SoundPicker/res/layout-watch/radio_with_work_badge.xml
+++ b/packages/SoundPicker/res/layout-watch/radio_with_work_badge.xml
@@ -13,7 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.providers.media.CheckedListItem xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.soundpicker.CheckedListItem xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="wrap_content"
     android:gravity="center_vertical"
@@ -44,4 +44,4 @@
         android:layout_centerVertical="true"
         android:scaleType="centerCrop"
         android:layout_marginRight="20dp" />
-</com.android.providers.media.CheckedListItem>
+</com.android.soundpicker.CheckedListItem>
diff --git a/packages/SoundPicker/res/layout/radio_with_work_badge.xml b/packages/SoundPicker/res/layout/radio_with_work_badge.xml
index e7d37ea..c8ca231 100644
--- a/packages/SoundPicker/res/layout/radio_with_work_badge.xml
+++ b/packages/SoundPicker/res/layout/radio_with_work_badge.xml
@@ -14,7 +14,7 @@
      limitations under the License.
 -->
 
-<com.android.providers.media.CheckedListItem xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.soundpicker.CheckedListItem xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="wrap_content"
     android:gravity="center_vertical"
@@ -45,4 +45,4 @@
         android:layout_centerVertical="true"
         android:scaleType="centerCrop"
         android:layout_marginRight="20dp" />
-</com.android.providers.media.CheckedListItem>
+</com.android.soundpicker.CheckedListItem>
diff --git a/packages/SoundPicker/src/com/android/soundpicker/CheckedListItem.java b/packages/SoundPicker/src/com/android/soundpicker/CheckedListItem.java
new file mode 100644
index 0000000..bde87cf
--- /dev/null
+++ b/packages/SoundPicker/src/com/android/soundpicker/CheckedListItem.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.soundpicker;
+
+import android.content.Context;
+import android.widget.Checkable;
+import android.widget.CheckedTextView;
+import android.widget.RelativeLayout;
+import android.util.AttributeSet;
+
+/**
+ * The {@link CheckedListItem} is a layout item that represents a ringtone, and is used in
+ * {@link RingtonePickerActivity}. It contains the ringtone's name, and a work badge to right of the
+ * name if the ringtone belongs to a work profile.
+ */
+public class CheckedListItem extends RelativeLayout implements Checkable {
+
+    public CheckedListItem(Context context) {
+        super(context);
+    }
+
+    public CheckedListItem(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public CheckedListItem(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public CheckedListItem(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    public void setChecked(boolean checked) {
+        getCheckedTextView().setChecked(checked);
+    }
+
+    @Override
+    public boolean isChecked() {
+        return getCheckedTextView().isChecked();
+    }
+
+    @Override
+    public void toggle() {
+        getCheckedTextView().toggle();
+    }
+
+    private CheckedTextView getCheckedTextView() {
+        return (CheckedTextView) findViewById(R.id.checked_text_view);
+    }
+
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/HomeControlsPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/HomeControlsPlugin.java
new file mode 100644
index 0000000..cac673f
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/HomeControlsPlugin.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 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 com.android.systemui.plugins;
+
+import android.view.ViewGroup;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+/**
+ * Test plugin for home controls
+ */
+@ProvidesInterface(action = HomeControlsPlugin.ACTION, version = HomeControlsPlugin.VERSION)
+public interface HomeControlsPlugin extends Plugin {
+
+    String ACTION = "com.android.systemui.action.PLUGIN_HOME_CONTROLS";
+    int VERSION = 1;
+
+    /**
+      * Pass the container for the plugin to use however it wants. Ideally the plugin impl
+      * will add home controls to this space.
+      */
+    void sendParentGroup(ViewGroup group);
+}
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 7d403b2..7a94008 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -55,6 +55,19 @@
             android:clipChildren="false"
             systemui:viewType="com.android.systemui.plugins.qs.QS" />
 
+        <!-- Temporary area to test out home controls -->
+        <LinearLayout
+            android:id="@+id/home_controls_layout"
+            android:layout_width="match_parent"
+            android:layout_height="125dp"
+            android:layout_gravity="@integer/notification_panel_layout_gravity"
+            android:visibility="gone"
+            android:padding="8dp"
+            android:layout_margin="5dp"
+            android:background="?android:attr/colorBackgroundFloating"
+            android:orientation="vertical">
+        </LinearLayout>
+
         <com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
             android:id="@+id/notification_stack_scroller"
             android:layout_marginTop="@dimen/notification_panel_margin_top"
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 858ed6d..ef171d3 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -50,7 +50,7 @@
  */
 @Singleton
 public class AppOpsControllerImpl implements AppOpsController,
-        AppOpsManager.OnOpActiveChangedListener,
+        AppOpsManager.OnOpActiveChangedInternalListener,
         AppOpsManager.OnOpNotedListener, Dumpable {
 
     private static final long NOTED_OP_TIME_DELAY_MS = 5000;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index c171730..c76cdcb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -53,6 +53,7 @@
 import android.view.WindowInsets;
 import android.view.accessibility.AccessibilityManager;
 import android.widget.FrameLayout;
+import android.widget.LinearLayout;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
@@ -68,10 +69,13 @@
 import com.android.systemui.fragments.FragmentHostManager;
 import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
 import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.HomeControlsPlugin;
+import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.qs.QSFragment;
+import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.GestureRecorder;
@@ -192,6 +196,7 @@
     private View mQsNavbarScrim;
     protected NotificationsQuickSettingsContainer mNotificationContainerParent;
     protected NotificationStackScrollLayout mNotificationStackScroller;
+    protected LinearLayout mHomeControlsLayout;
     private boolean mAnimateNextPositionUpdate;
 
     private int mTrackingPointer;
@@ -450,6 +455,7 @@
         mBigClockContainer = findViewById(R.id.big_clock_container);
         keyguardClockSwitch.setBigClockContainer(mBigClockContainer);
 
+        mHomeControlsLayout = findViewById(R.id.home_controls_layout);
         mNotificationContainerParent = findViewById(R.id.notification_container_parent);
         mNotificationStackScroller = findViewById(R.id.notification_stack_scroller);
         mNotificationStackScroller.setOnHeightChangedListener(this);
@@ -480,6 +486,21 @@
                 }
             }
         });
+
+        Dependency.get(PluginManager.class).addPluginListener(
+                new PluginListener<HomeControlsPlugin>() {
+
+                    @Override
+                    public void onPluginConnected(HomeControlsPlugin plugin,
+                                                  Context pluginContext) {
+                        plugin.sendParentGroup(mHomeControlsLayout);
+                    }
+
+                    @Override
+                    public void onPluginDisconnected(HomeControlsPlugin plugin) {
+
+                    }
+                }, HomeControlsPlugin.class, false);
     }
 
     @Override
@@ -1270,9 +1291,11 @@
             if (mQsExpandImmediate) {
                 mNotificationStackScroller.setVisibility(View.GONE);
                 mQsFrame.setVisibility(View.VISIBLE);
+                mHomeControlsLayout.setVisibility(View.VISIBLE);
             } else {
                 mNotificationStackScroller.setVisibility(View.VISIBLE);
                 mQsFrame.setVisibility(View.GONE);
+                mHomeControlsLayout.setVisibility(View.GONE);
             }
         }
         return false;
@@ -1551,6 +1574,7 @@
         if (mKeyguardShowing && isQsSplitEnabled()) {
             mNotificationStackScroller.setVisibility(View.VISIBLE);
             mQsFrame.setVisibility(View.VISIBLE);
+            mHomeControlsLayout.setVisibility(View.GONE);
         }
 
         if (oldState == StatusBarState.KEYGUARD
@@ -2099,8 +2123,10 @@
                 t = (expandedHeight - panelHeightQsCollapsed)
                         / (panelHeightQsExpanded - panelHeightQsCollapsed);
             }
-            setQsExpansion(mQsMinExpansionHeight
-                    + t * (mQsMaxExpansionHeight - mQsMinExpansionHeight));
+            float targetHeight = mQsMinExpansionHeight
+                    + t * (mQsMaxExpansionHeight - mQsMinExpansionHeight);
+            setQsExpansion(targetHeight);
+            mHomeControlsLayout.setTranslationY(targetHeight);
         }
         updateExpandedHeight(expandedHeight);
         updateHeader();
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
index b88b24e..2032109 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
@@ -468,7 +468,13 @@
         }
     }
 
-    boolean hasPermission(String permission) {
+    /**
+     * Permission check to caller.
+     *
+     * @param permission The permission to check
+     * @return true if caller has permission
+     */
+    public boolean hasPermission(@NonNull String permission) {
         return mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED;
     }
 
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index a1b6d49..1da5bc6 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -38,6 +38,7 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.IndentingPrintWriter;
 
 import java.io.File;
@@ -263,7 +264,8 @@
                     populateAllPackagesCacheIfNeeded();
                     mContext.unregisterReceiver(this);
                 }
-            }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
+            }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED), /* broadcastPermission */ null,
+                    BackgroundThread.getHandler());
         }
 
         private void populateAllPackagesCacheIfNeeded() {
diff --git a/services/tests/servicestests/src/com/android/server/appop/AppOpsActiveWatcherTest.java b/services/tests/servicestests/src/com/android/server/appop/AppOpsActiveWatcherTest.java
index 65c5781..41142f6 100644
--- a/services/tests/servicestests/src/com/android/server/appop/AppOpsActiveWatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/appop/AppOpsActiveWatcherTest.java
@@ -57,15 +57,15 @@
 
         // Start watching active ops
         final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
-        appOpsManager.startWatchingActive(new int[] {AppOpsManager.OP_CAMERA,
-                AppOpsManager.OP_RECORD_AUDIO}, listener);
+        appOpsManager.startWatchingActive(new String[] {AppOpsManager.OPSTR_CAMERA,
+                AppOpsManager.OPSTR_RECORD_AUDIO}, getContext().getMainExecutor(), listener);
 
         // Start the op
         appOpsManager.startOp(AppOpsManager.OP_CAMERA);
 
         // Verify that we got called for the op being active
         verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
-                .times(1)).onOpActiveChanged(eq(AppOpsManager.OP_CAMERA),
+                .times(1)).onOpActiveChanged(eq(AppOpsManager.OPSTR_CAMERA),
                 eq(Process.myUid()), eq(getContext().getPackageName()), eq(true));
 
         // This should be the only callback we got
@@ -83,7 +83,7 @@
 
         // Verify that we got called for the op being active
         verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
-                .times(1)).onOpActiveChanged(eq(AppOpsManager.OP_CAMERA),
+                .times(1)).onOpActiveChanged(eq(AppOpsManager.OPSTR_CAMERA),
                 eq(Process.myUid()), eq(getContext().getPackageName()), eq(false));
 
         // Verify that the op is not active
@@ -155,4 +155,4 @@
     private static Context getContext() {
         return InstrumentationRegistry.getContext();
     }
-}
\ No newline at end of file
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/ByteStream.java b/services/usb/java/com/android/server/usb/descriptors/ByteStream.java
index 1e823b6..56dc3e0 100644
--- a/services/usb/java/com/android/server/usb/descriptors/ByteStream.java
+++ b/services/usb/java/com/android/server/usb/descriptors/ByteStream.java
@@ -185,12 +185,14 @@
             // Positive offsets only
             throw new IllegalArgumentException();
         }
-        // do arithmetic and comparison in long to ovoid potention integer overflow
+        // do arithmetic and comparison in long to avoid potential integer overflow
         long longNewIndex = (long) mIndex + (long) numBytes;
-        if (longNewIndex < (long) mBytes.length) {
+        if (longNewIndex <= (long) mBytes.length) {
             mReadCount += numBytes;
             mIndex += numBytes;
         } else {
+            // Position the stream to the end so available() will return 0
+            mIndex = mBytes.length;
             throw new IndexOutOfBoundsException();
         }
     }
@@ -210,6 +212,7 @@
             mReadCount -= numBytes;
             mIndex -= numBytes;
         } else {
+            mIndex = 0;
             throw new IndexOutOfBoundsException();
         }
     }
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java
index 7ebccf3..0535d71 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java
@@ -45,7 +45,6 @@
     @Override
     public int parseRawDescriptors(ByteStream stream) {
         mSubtype = stream.getByte();
-
         return mLength;
     }
 
@@ -55,12 +54,21 @@
         int subClass = interfaceDesc.getUsbSubclass();
         switch (subClass) {
             case AUDIO_AUDIOCONTROL:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "---> AUDIO_AUDIOCONTROL");
+                }
                 return new UsbACAudioControlEndpoint(length, type, subClass);
 
             case AUDIO_AUDIOSTREAMING:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "---> AUDIO_AUDIOSTREAMING");
+                }
                 return new UsbACAudioStreamEndpoint(length, type, subClass);
 
             case AUDIO_MIDISTREAMING:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "---> AUDIO_MIDISTREAMING");
+                }
                 return new UsbACMidiEndpoint(length, type, subClass);
 
             default:
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACInterface.java b/services/usb/java/com/android/server/usb/descriptors/UsbACInterface.java
index 38c12a1..82fbfb8 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbACInterface.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACInterface.java
@@ -100,8 +100,14 @@
         switch (subtype) {
             case ACI_HEADER:
             {
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> ACI_HEADER");
+                }
                 int acInterfaceSpec = stream.unpackUsbShort();
                 parser.setACInterfaceSpec(acInterfaceSpec);
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "  acInterfaceSpec:0x" + Integer.toHexString(acInterfaceSpec));
+                }
                 if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
                     return new Usb20ACHeader(length, type, subtype, subClass, acInterfaceSpec);
                 } else {
@@ -111,7 +117,13 @@
 
             case ACI_INPUT_TERMINAL:
             {
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> ACI_INPUT_TERMINAL");
+                }
                 int acInterfaceSpec = parser.getACInterfaceSpec();
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "  acInterfaceSpec:0x" + Integer.toHexString(acInterfaceSpec));
+                }
                 if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
                     return new Usb20ACInputTerminal(length, type, subtype, subClass);
                 } else {
@@ -121,7 +133,13 @@
 
             case ACI_OUTPUT_TERMINAL:
             {
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> ACI_OUTPUT_TERMINAL");
+                }
                 int acInterfaceSpec = parser.getACInterfaceSpec();
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "  acInterfaceSpec:0x" + Integer.toHexString(acInterfaceSpec));
+                }
                 if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
                     return new Usb20ACOutputTerminal(length, type, subtype, subClass);
                 } else {
@@ -130,14 +148,26 @@
             }
 
             case ACI_SELECTOR_UNIT:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> ACI_SELECTOR_UNIT");
+                }
                 return new UsbACSelectorUnit(length, type, subtype, subClass);
 
             case ACI_FEATURE_UNIT:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> ACI_FEATURE_UNIT");
+                }
                 return new UsbACFeatureUnit(length, type, subtype, subClass);
 
             case ACI_MIXER_UNIT:
             {
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> ACI_MIXER_UNIT");
+                }
                 int acInterfaceSpec = parser.getACInterfaceSpec();
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "  acInterfaceSpec:0x" + Integer.toHexString(acInterfaceSpec));
+                }
                 if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
                     return new Usb20ACMixerUnit(length, type, subtype, subClass);
                 } else {
@@ -215,14 +245,23 @@
         int subClass = interfaceDesc.getUsbSubclass();
         switch (subClass) {
             case AUDIO_AUDIOCONTROL:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "  AUDIO_AUDIOCONTROL");
+                }
                 return allocAudioControlDescriptor(
                         parser, stream, length, type, subtype, subClass);
 
             case AUDIO_AUDIOSTREAMING:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "  AUDIO_AUDIOSTREAMING");
+                }
                 return allocAudioStreamingDescriptor(
                         parser, stream, length, type, subtype, subClass);
 
             case AUDIO_MIDISTREAMING:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "  AUDIO_MIDISTREAMING");
+                }
                 return allocMidiStreamingDescriptor(length, type, subtype, subClass);
 
             default:
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
index 639aa4e..de2dd10 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
@@ -30,7 +30,6 @@
  */
 public final class UsbConfigDescriptor extends UsbDescriptor {
     private static final String TAG = "UsbConfigDescriptor";
-    private static final boolean DEBUG = false;
 
     private int mTotalLength;    // 2:2 Total length in bytes of data returned
     private byte mNumInterfaces; // 4:1 Number of Interfaces
@@ -79,14 +78,14 @@
     }
 
     UsbConfiguration toAndroid(UsbDescriptorParser parser) {
-        if (DEBUG) {
+        if (UsbDescriptorParser.DEBUG) {
             Log.d(TAG, "  toAndroid()");
         }
         String name = parser.getDescriptorString(mConfigIndex);
         UsbConfiguration config = new
                 UsbConfiguration(mConfigValue, name, mAttribs, mMaxPower);
         UsbInterface[] interfaces = new UsbInterface[mInterfaceDescriptors.size()];
-        if (DEBUG) {
+        if (UsbDescriptorParser.DEBUG) {
             Log.d(TAG, "    " + mInterfaceDescriptors.size() + " interfaces.");
         }
         for (int index = 0; index < mInterfaceDescriptors.size(); index++) {
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java
index ff67667..44422a2 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java
@@ -43,7 +43,7 @@
     protected int mHierarchyLevel;
 
     protected final int mLength;    // 0:1 bLength Number Size of the Descriptor in Bytes (18 bytes)
-                                    // we store this as an int because Java bytes are SIGNED.
+    // we store this as an int because Java bytes are SIGNED.
     protected final byte mType;     // 1:1 bDescriptorType Constant Device Descriptor (0x01)
 
     private byte[] mRawData;
@@ -52,11 +52,11 @@
     private static byte[] sStringBuffer = new byte[SIZE_STRINGBUFFER];
 
     // Status
-    public static final int STATUS_UNPARSED         = 0;
-    public static final int STATUS_PARSED_OK        = 1;
-    public static final int STATUS_PARSED_UNDERRUN  = 2;
-    public static final int STATUS_PARSED_OVERRUN   = 3;
-    public static final int STATUS_PARSE_EXCEPTION  = 4;
+    public static final int STATUS_UNPARSED = 0;
+    public static final int STATUS_PARSED_OK = 1;
+    public static final int STATUS_PARSED_UNDERRUN = 2;
+    public static final int STATUS_PARSED_OVERRUN = 3;
+    public static final int STATUS_PARSE_EXCEPTION = 4;
 
     private int mStatus = STATUS_UNPARSED;
 
@@ -78,53 +78,53 @@
     public static final byte DESCRIPTORTYPE_HID = 0x21;                // 33
     public static final byte DESCRIPTORTYPE_REPORT = 0x22;             // 34
     public static final byte DESCRIPTORTYPE_PHYSICAL = 0x23;           // 35
-    public static final byte DESCRIPTORTYPE_AUDIO_INTERFACE = 0x24;    // 36
-    public static final byte DESCRIPTORTYPE_AUDIO_ENDPOINT = 0x25;     // 37
+    public static final byte DESCRIPTORTYPE_CLASSSPECIFIC_INTERFACE = 0x24;    // 36
+    public static final byte DESCRIPTORTYPE_CLASSSPECIFIC_ENDPOINT = 0x25;     // 37
     public static final byte DESCRIPTORTYPE_HUB = 0x29;                // 41
     public static final byte DESCRIPTORTYPE_SUPERSPEED_HUB = 0x2A;     // 42
     public static final byte DESCRIPTORTYPE_ENDPOINT_COMPANION = 0x30; // 48
 
     // Class IDs
-    public static final int CLASSID_DEVICE  =      0x00;
-    public static final int CLASSID_AUDIO =        0x01;
-    public static final int CLASSID_COM =          0x02;
-    public static final int CLASSID_HID =          0x03;
+    public static final int CLASSID_DEVICE = 0x00;
+    public static final int CLASSID_AUDIO = 0x01;
+    public static final int CLASSID_COM = 0x02;
+    public static final int CLASSID_HID = 0x03;
     // public static final int CLASSID_??? =       0x04;
-    public static final int CLASSID_PHYSICAL =     0x05;
-    public static final int CLASSID_IMAGE =        0x06;
-    public static final int CLASSID_PRINTER =      0x07;
-    public static final int CLASSID_STORAGE =      0x08;
-    public static final int CLASSID_HUB =          0x09;
-    public static final int CLASSID_CDC_CONTROL =  0x0A;
-    public static final int CLASSID_SMART_CARD =   0x0B;
+    public static final int CLASSID_PHYSICAL = 0x05;
+    public static final int CLASSID_IMAGE = 0x06;
+    public static final int CLASSID_PRINTER = 0x07;
+    public static final int CLASSID_STORAGE = 0x08;
+    public static final int CLASSID_HUB = 0x09;
+    public static final int CLASSID_CDC_CONTROL = 0x0A;
+    public static final int CLASSID_SMART_CARD = 0x0B;
     //public static final int CLASSID_??? =        0x0C;
-    public static final int CLASSID_SECURITY =     0x0D;
-    public static final int CLASSID_VIDEO =        0x0E;
-    public static final int CLASSID_HEALTHCARE =   0x0F;
-    public static final int CLASSID_AUDIOVIDEO =   0x10;
-    public static final int CLASSID_BILLBOARD =    0x11;
-    public static final int CLASSID_TYPECBRIDGE =  0x12;
-    public static final int CLASSID_DIAGNOSTIC =   0xDC;
-    public static final int CLASSID_WIRELESS =     0xE0;
-    public static final int CLASSID_MISC =         0xEF;
-    public static final int CLASSID_APPSPECIFIC =  0xFE;
+    public static final int CLASSID_SECURITY = 0x0D;
+    public static final int CLASSID_VIDEO = 0x0E;
+    public static final int CLASSID_HEALTHCARE = 0x0F;
+    public static final int CLASSID_AUDIOVIDEO = 0x10;
+    public static final int CLASSID_BILLBOARD = 0x11;
+    public static final int CLASSID_TYPECBRIDGE = 0x12;
+    public static final int CLASSID_DIAGNOSTIC = 0xDC;
+    public static final int CLASSID_WIRELESS = 0xE0;
+    public static final int CLASSID_MISC = 0xEF;
+    public static final int CLASSID_APPSPECIFIC = 0xFE;
     public static final int CLASSID_VENDSPECIFIC = 0xFF;
 
     // Audio Subclass codes
-    public static final int AUDIO_SUBCLASS_UNDEFINED   = 0x00;
-    public static final int AUDIO_AUDIOCONTROL         = 0x01;
-    public static final int AUDIO_AUDIOSTREAMING       = 0x02;
-    public static final int AUDIO_MIDISTREAMING        = 0x03;
+    public static final int AUDIO_SUBCLASS_UNDEFINED = 0x00;
+    public static final int AUDIO_AUDIOCONTROL = 0x01;
+    public static final int AUDIO_AUDIOSTREAMING = 0x02;
+    public static final int AUDIO_MIDISTREAMING = 0x03;
 
     // Request IDs
-    public static final int REQUEST_GET_STATUS         = 0x00;
-    public static final int REQUEST_CLEAR_FEATURE      = 0x01;
-    public static final int REQUEST_SET_FEATURE        = 0x03;
-    public static final int REQUEST_GET_ADDRESS        = 0x05;
-    public static final int REQUEST_GET_DESCRIPTOR     = 0x06;
-    public static final int REQUEST_SET_DESCRIPTOR     = 0x07;
-    public static final int REQUEST_GET_CONFIGURATION  = 0x08;
-    public static final int REQUEST_SET_CONFIGURATION  = 0x09;
+    public static final int REQUEST_GET_STATUS = 0x00;
+    public static final int REQUEST_CLEAR_FEATURE = 0x01;
+    public static final int REQUEST_SET_FEATURE = 0x03;
+    public static final int REQUEST_GET_ADDRESS = 0x05;
+    public static final int REQUEST_GET_DESCRIPTOR = 0x06;
+    public static final int REQUEST_SET_DESCRIPTOR = 0x07;
+    public static final int REQUEST_GET_CONFIGURATION = 0x08;
+    public static final int REQUEST_SET_CONFIGURATION = 0x09;
 
     // USB control transfer timeout
     public static final int USB_CONTROL_TRANSFER_TIMEOUT_MS = 200;
@@ -163,7 +163,6 @@
     public int getOverUnderRunCount() {
         return mOverUnderRunCount;
     }
-
     public String getStatusString() {
         return sStatusStrings[mStatus];
     }
@@ -278,4 +277,24 @@
                 + " Len: " + getLength();
         canvas.writeParagraph(text, false);
     }
+
+    /*
+     * Logging Helpers
+     */
+    static String getDescriptorName(byte descriptorType, int descriptorLength) {
+        String name = UsbStrings.getDescriptorName(descriptorType);
+        if (name != null) {
+            return name;
+        } else {
+            return "Unknown Descriptor Type " + descriptorType
+                + " 0x" + Integer.toHexString(descriptorType)
+                + " length:" + descriptorLength;
+        }
+    }
+
+    static void logDescriptorName(byte descriptorType, int descriptorLength) {
+        if (UsbDescriptorParser.DEBUG) {
+            Log.d(TAG, "----> " + getDescriptorName(descriptorType, descriptorLength));
+        }
+    }
 }
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
index c021101..4f62d5a 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -26,7 +26,7 @@
  */
 public final class UsbDescriptorParser {
     private static final String TAG = "UsbDescriptorParser";
-    private static final boolean DEBUG = false;
+    public static final boolean DEBUG = false;
 
     private final String mDeviceAddr;
 
@@ -115,6 +115,8 @@
         int length = stream.getUnsignedByte();
         byte type = stream.getByte();
 
+        UsbDescriptor.logDescriptorName(type, length);
+
         UsbDescriptor descriptor = null;
         switch (type) {
             /*
@@ -174,14 +176,56 @@
                 break;
 
             /*
-             * Audio Class Specific
+             * Various Class Specific
              */
-            case UsbDescriptor.DESCRIPTORTYPE_AUDIO_INTERFACE:
-                descriptor = UsbACInterface.allocDescriptor(this, stream, length, type);
+            case UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_INTERFACE:
+                if (mCurInterfaceDescriptor != null) {
+                    switch (mCurInterfaceDescriptor.getUsbClass()) {
+                        case UsbDescriptor.CLASSID_AUDIO:
+                            descriptor = UsbACInterface.allocDescriptor(this, stream, length, type);
+                            break;
+
+                        case UsbDescriptor.CLASSID_VIDEO:
+                            Log.d(TAG, "  UsbDescriptor.CLASSID_VIDEO subType:0x"
+                                    + Integer.toHexString(stream.getByte()));
+                            descriptor = UsbVCInterface.allocDescriptor(this, stream, length, type);
+                            break;
+
+                        case UsbDescriptor.CLASSID_AUDIOVIDEO:
+                            Log.d(TAG, "  UsbDescriptor.CLASSID_AUDIOVIDEO subType:0x"
+                                    + Integer.toHexString(stream.getByte()));
+                            break;
+
+                        default:
+                            Log.d(TAG, "  Unparsed Class-specific Interface:0x"
+                                    + Integer.toHexString(mCurInterfaceDescriptor.getUsbClass()));
+                            break;
+                    }
+                }
                 break;
 
-            case UsbDescriptor.DESCRIPTORTYPE_AUDIO_ENDPOINT:
-                descriptor = UsbACEndpoint.allocDescriptor(this, length, type);
+            case UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_ENDPOINT:
+                if (mCurInterfaceDescriptor != null) {
+                    switch (mCurInterfaceDescriptor.getUsbClass()) {
+                        case UsbDescriptor.CLASSID_AUDIO:
+                            descriptor = UsbACEndpoint.allocDescriptor(this, length, type);
+                            break;
+                        case UsbDescriptor.CLASSID_VIDEO:
+                            Log.d(TAG, "UsbDescriptor.CLASSID_VIDEO subType:0x"
+                                    + Integer.toHexString(stream.getByte()));
+                            descriptor = UsbVCEndpoint.allocDescriptor(this, length, type);
+                            break;
+
+                        case UsbDescriptor.CLASSID_AUDIOVIDEO:
+                            Log.d(TAG, "UsbDescriptor.CLASSID_AUDIOVIDEO subType:0x"
+                                    + Integer.toHexString(stream.getByte()));
+                            break;
+                        default:
+                            Log.d(TAG, "  Unparsed Class-specific Endpoint:0x"
+                                    + Integer.toHexString(mCurInterfaceDescriptor.getUsbClass()));
+                            break;
+                    }
+                }
                 break;
 
             default:
@@ -190,8 +234,6 @@
 
         if (descriptor == null) {
             // Unknown Descriptor
-            Log.i(TAG, "Unknown Descriptor len: " + length + " type:0x"
-                    + Integer.toHexString(type));
             descriptor = new UsbUnknown(length, type);
         }
 
@@ -210,10 +252,6 @@
      * @hide
      */
     public void parseDescriptors(byte[] descriptors) {
-        if (DEBUG) {
-            Log.d(TAG, "parseDescriptors() - start");
-        }
-
         ByteStream stream = new ByteStream(descriptors);
         while (stream.available() > 0) {
             UsbDescriptor descriptor = null;
@@ -325,8 +363,8 @@
     public ArrayList<UsbDescriptor> getACInterfaceDescriptors(byte subtype, int subclass) {
         ArrayList<UsbDescriptor> list = new ArrayList<UsbDescriptor>();
         for (UsbDescriptor descriptor : mDescriptors) {
-            if (descriptor.getType() == UsbDescriptor.DESCRIPTORTYPE_AUDIO_INTERFACE) {
-                // ensure that this isn't an unrecognized DESCRIPTORTYPE_AUDIO_INTERFACE
+            if (descriptor.getType() == UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_INTERFACE) {
+                // ensure that this isn't an unrecognized DESCRIPTORTYPE_CLASSSPECIFIC_INTERFACE
                 if (descriptor instanceof UsbACInterface) {
                     UsbACInterface acDescriptor = (UsbACInterface) descriptor;
                     if (acDescriptor.getSubtype() == subtype
@@ -334,8 +372,8 @@
                         list.add(descriptor);
                     }
                 } else {
-                    Log.w(TAG, "Unrecognized Audio Interface l: " + descriptor.getLength()
-                            + " t:0x" + Integer.toHexString(descriptor.getType()));
+                    Log.w(TAG, "Unrecognized Audio Interface len: " + descriptor.getLength()
+                            + " type:0x" + Integer.toHexString(descriptor.getType()));
                 }
             }
         }
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
index f50b9cb..e6e10fe 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
@@ -31,7 +31,6 @@
  */
 public final class UsbDeviceDescriptor extends UsbDescriptor {
     private static final String TAG = "UsbDeviceDescriptor";
-    private static final boolean DEBUG = false;
 
     public static final int USBSPEC_1_0 = 0x0100;
     public static final int USBSPEC_1_1 = 0x0110;
@@ -136,19 +135,19 @@
      * @hide
      */
     public UsbDevice.Builder toAndroid(UsbDescriptorParser parser) {
-        if (DEBUG) {
+        if (UsbDescriptorParser.DEBUG) {
             Log.d(TAG, "toAndroid()");
         }
 
         String mfgName = getMfgString(parser);
         String prodName = getProductString(parser);
-        if (DEBUG) {
+        if (UsbDescriptorParser.DEBUG) {
             Log.d(TAG, "  mfgName:" + mfgName + " prodName:" + prodName);
         }
 
         String versionString = getDeviceReleaseString();
         String serialStr = getSerialString(parser);
-        if (DEBUG) {
+        if (UsbDescriptorParser.DEBUG) {
             Log.d(TAG, "  versionString:" + versionString + " serialStr:" + serialStr);
         }
 
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
index 4da31ea..5eb0a2f 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
@@ -27,7 +27,6 @@
  */
 public class UsbEndpointDescriptor extends UsbDescriptor {
     private static final String TAG = "UsbEndpointDescriptor";
-    private static final boolean DEBUG = false;
 
     public static final int MASK_ENDPOINT_ADDRESS = 0b000000000001111;
     public static final int MASK_ENDPOINT_DIRECTION = (byte) 0b0000000010000000;
@@ -110,7 +109,7 @@
     }
 
     /* package */ UsbEndpoint toAndroid(UsbDescriptorParser parser) {
-        if (DEBUG) {
+        if (UsbDescriptorParser.DEBUG) {
             Log.d(TAG, "toAndroid() type:"
                     + Integer.toHexString(mAttributes & MASK_ATTRIBS_TRANSTYPE)
                     + " sync:" + Integer.toHexString(mAttributes & MASK_ATTRIBS_SYNCTYPE)
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
index 632e3dc..1dc6069 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
@@ -31,7 +31,6 @@
  */
 public class UsbInterfaceDescriptor extends UsbDescriptor {
     private static final String TAG = "UsbInterfaceDescriptor";
-    private static final boolean DEBUG = false;
 
     protected int mInterfaceNumber;   // 2:1 Number of Interface
     protected byte mAlternateSetting; // 3:1 Value used to select alternative setting
@@ -95,7 +94,7 @@
     }
 
     UsbInterface toAndroid(UsbDescriptorParser parser) {
-        if (DEBUG) {
+        if (UsbDescriptorParser.DEBUG) {
             Log.d(TAG, "toAndroid() class:" + Integer.toHexString(mUsbClass)
                     + " subclass:" + Integer.toHexString(mUsbSubclass)
                     + " " + mEndpointDescriptors.size() + " endpoints.");
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCEndpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCEndpoint.java
new file mode 100644
index 0000000..39fbc0d
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCEndpoint.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 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 com.android.server.usb.descriptors;
+
+import android.util.Log;
+
+/**
+ * @hide
+ * A video class-specific Endpoint
+ * see
+ */
+abstract class UsbVCEndpoint extends UsbDescriptor {
+    private static final String TAG = "UsbVCEndpoint";
+
+    UsbVCEndpoint(int length, byte type, int subclass) {
+        super(length, type);
+        // mSubclass = subclass;
+    }
+
+    public static UsbDescriptor allocDescriptor(UsbDescriptorParser parser,
+                                                int length, byte type) {
+        UsbInterfaceDescriptor interfaceDesc = parser.getCurInterface();
+        int subClass = interfaceDesc.getUsbSubclass();
+        switch (subClass) {
+//            case AUDIO_AUDIOCONTROL:
+//                if (UsbDescriptorParser.DEBUG) {
+//                    Log.i(TAG, "---> AUDIO_AUDIOCONTROL");
+//                }
+//                return new UsbACAudioControlEndpoint(length, type, subClass);
+//
+//            case AUDIO_AUDIOSTREAMING:
+//                if (UsbDescriptorParser.DEBUG) {
+//                    Log.i(TAG, "---> AUDIO_AUDIOSTREAMING");
+//                }
+//                return new UsbACAudioStreamEndpoint(length, type, subClass);
+//
+//            case AUDIO_MIDISTREAMING:
+//                if (UsbDescriptorParser.DEBUG) {
+//                    Log.i(TAG, "---> AUDIO_MIDISTREAMING");
+//                }
+//                return new UsbACMidiEndpoint(length, type, subClass);
+
+            default:
+                Log.w(TAG, "Unknown Video Class Endpoint id:0x" + Integer.toHexString(subClass));
+                return null;
+        }
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCInterface.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCInterface.java
new file mode 100644
index 0000000..c9eb1ec
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCInterface.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 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 com.android.server.usb.descriptors;
+
+import android.util.Log;
+
+/**
+ * @hide
+ * A video class-specific Interface.
+ * see USB_Video_Class_1.1.pdf, section 3.7.2
+ */
+public abstract class UsbVCInterface extends UsbDescriptor {
+    private static final String TAG = "UsbVCInterface";
+
+    // Class-specific Video Subtypes
+    public static final byte VCI_UNDEFINED          = 0x00;
+    public static final byte VCI_VEADER             = 0x01;
+    public static final byte VCI_INPUT_TERMINAL     = 0x02;
+    public static final byte VCI_VOUTPUT_TERMINAL   = 0x03;
+    public static final byte VCI_SELECTOR_UNIT      = 0x04;
+    public static final byte VCI_VROCESSING_UNIT    = 0x05;
+    public static final byte VCI_VEXTENSION_UNIT    = 0x06;
+
+   // See “Universal Serial Bus Device Class Definition for Video
+    protected final byte mSubtype;  // 2:1 HEADER descriptor subtype
+    protected final int mSubclass;  // from the mSubclass member of the
+    // "enclosing" Interface Descriptor
+
+    public UsbVCInterface(int length, byte type, byte subtype, int subclass) {
+        super(length, type);
+        mSubtype = subtype;
+        mSubclass = subclass;
+    }
+
+    /**
+     * Allocates an audio class interface subtype based on subtype and subclass.
+     */
+    public static UsbDescriptor allocDescriptor(UsbDescriptorParser parser, ByteStream stream,
+                                                int length, byte type) {
+        byte subtype = stream.getByte();
+        UsbInterfaceDescriptor interfaceDesc = parser.getCurInterface();
+        int subClass = interfaceDesc.getUsbSubclass();
+        if (UsbDescriptorParser.DEBUG) {
+            Log.d(TAG, "  Video Class-specific Interface subClass:0x"
+                    + Integer.toHexString(subClass));
+        }
+        switch (subClass) {
+            // TODO - Create descriptor classes and parse these...
+            case VCI_UNDEFINED:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> VCI_UNDEFINED");
+                }
+                break;
+
+            case VCI_VEADER:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> VCI_VEADER");
+                }
+                break;
+
+            case VCI_INPUT_TERMINAL:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> VCI_INPUT_TERMINAL");
+                }
+                break;
+
+            case VCI_VOUTPUT_TERMINAL:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> VCI_VOUTPUT_TERMINAL");
+                }
+                break;
+
+            case VCI_SELECTOR_UNIT:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> VCI_SELECTOR_UNIT");
+                }
+                break;
+
+            case VCI_VROCESSING_UNIT:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> VCI_VROCESSING_UNIT");
+                }
+                break;
+
+            case VCI_VEXTENSION_UNIT:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> VCI_VEXTENSION_UNIT");
+                }
+                break;
+
+            default:
+                Log.w(TAG, "Unknown Video Class Interface Subclass: 0x"
+                        + Integer.toHexString(subClass));
+                return null;
+        }
+
+        return null;
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/report/UsbStrings.java b/services/usb/java/com/android/server/usb/descriptors/report/UsbStrings.java
index fb4576a..918ba2cc 100644
--- a/services/usb/java/com/android/server/usb/descriptors/report/UsbStrings.java
+++ b/services/usb/java/com/android/server/usb/descriptors/report/UsbStrings.java
@@ -56,9 +56,10 @@
         sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_HID, "HID");
         sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_REPORT, "Report");
         sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_PHYSICAL, "Physical");
-        sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_AUDIO_INTERFACE,
-                "Audio Class Interface");
-        sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_AUDIO_ENDPOINT, "Audio Class Endpoint");
+        sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_INTERFACE,
+                "Class-specific Interface");
+        sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_ENDPOINT,
+                "Class-specific Endpoint");
         sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_HUB, "Hub");
         sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_SUPERSPEED_HUB, "Superspeed Hub");
         sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_ENDPOINT_COMPANION,
diff --git a/services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsTree.java b/services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsTree.java
index 1aa30fa..72fa897 100644
--- a/services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsTree.java
+++ b/services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsTree.java
@@ -126,11 +126,13 @@
                 //
                 // Audio Class Descriptors
                 //
-                case UsbDescriptor.DESCRIPTORTYPE_AUDIO_INTERFACE:
-                    addACInterface((UsbACInterface) descriptor);
+                case UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_INTERFACE:
+                    //TODO: This needs to be parsed out to Audio/Video...
+                    // addACInterface((UsbACInterface) descriptor);
                     break;
 
-                case UsbDescriptor.DESCRIPTORTYPE_AUDIO_ENDPOINT:
+                case UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_ENDPOINT:
+                    //TODO: This needs to be parsed out to Audio/Video...
                     break;
             }
         }
diff --git a/telephony/java/com/android/internal/telephony/CbGeoUtils.java b/telephony/java/com/android/internal/telephony/CbGeoUtils.java
new file mode 100644
index 0000000..c973b67
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/CbGeoUtils.java
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 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 com.android.internal.telephony;
+
+import android.annotation.NonNull;
+import android.telephony.Rlog;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+
+/**
+ * This utils class is specifically used for geo-targeting of CellBroadcast messages.
+ * The coordinates used by this utils class are latitude and longitude, but some algorithms in this
+ * class only use them as coordinates on plane, so the calculation will be inaccurate. So don't use
+ * this class for anything other then geo-targeting of cellbroadcast messages.
+ */
+public class CbGeoUtils {
+    /** Geometric interface. */
+    public interface Geometry {
+        /**
+         * Determines if the given point {@code p} is inside the geometry.
+         * @param p point in latitude, longitude format.
+         * @return {@code True} if the given point is inside the geometry.
+         */
+        boolean contains(LatLng p);
+    }
+
+    /**
+     * Tolerance for determining if the value is 0. If the absolute value of a value is less than
+     * this tolerance, it will be treated as 0.
+     */
+    public static final double EPS = 1e-7;
+
+    /** The radius of earth. */
+    public static final int EARTH_RADIUS_METER = 6371 * 1000;
+
+    private static final String TAG = "CbGeoUtils";
+
+    /** The identifier of geometry in the encoded string. */
+    private static final String CIRCLE_SYMBOL = "circle";
+    private static final String POLYGON_SYMBOL = "polygon";
+
+    /** Point represent by (latitude, longitude). */
+    public static class LatLng {
+        public final double lat;
+        public final double lng;
+
+        /**
+         * Constructor.
+         * @param lat latitude, range [-90, 90]
+         * @param lng longitude, range [-180, 180]
+         */
+        public LatLng(double lat, double lng) {
+            this.lat = lat;
+            this.lng = lng;
+        }
+
+        /**
+         * @param p the point use to calculate the subtraction result.
+         * @return the result of this point subtract the given point {@code p}.
+         */
+        public LatLng subtract(LatLng p) {
+            return new LatLng(lat - p.lat, lng - p.lng);
+        }
+
+        /**
+         * Calculate the distance in meter between this point and the given point {@code p}.
+         * @param p the point use to calculate the distance.
+         * @return the distance in meter.
+         */
+        public double distance(LatLng p) {
+            double dlat = Math.sin(0.5 * Math.toRadians(lat - p.lat));
+            double dlng = Math.sin(0.5 * Math.toRadians(lng - p.lng));
+            double x = dlat * dlat
+                    + dlng * dlng * Math.cos(Math.toRadians(lat)) * Math.cos(Math.toRadians(p.lat));
+            return 2 * Math.atan2(Math.sqrt(x), Math.sqrt(1 - x)) * EARTH_RADIUS_METER;
+        }
+    }
+
+    /**
+     * The class represents a simple polygon with at least 3 points.
+     */
+    public static class Polygon implements Geometry {
+        /**
+         * In order to reduce the loss of precision in floating point calculations, all vertices
+         * of the polygon are scaled. Set the value of scale to 1000 can take into account the
+         * actual distance accuracy of 1 meter if the EPS is 1e-7 during the calculation.
+         */
+        private static final double SCALE = 1000.0;
+
+        private final List<LatLng> mVertices;
+        private final List<Point> mScaledVertices;
+        private final LatLng mOrigin;
+
+        /**
+         * Constructs a simple polygon from the given vertices. The adjacent two vertices are
+         * connected to form an edge of the polygon. The polygon has at least 3 vertices, and the
+         * last vertices and the first vertices must be adjacent.
+         *
+         * The longitude difference in the vertices should be less than 180 degree.
+         */
+        public Polygon(@NonNull List<LatLng> vertices) {
+            mVertices = vertices;
+
+            // Find the point with smallest longitude as the mOrigin point.
+            int idx = 0;
+            for (int i = 1; i < vertices.size(); i++) {
+                if (vertices.get(i).lng < vertices.get(idx).lng) {
+                    idx = i;
+                }
+            }
+            mOrigin = vertices.get(idx);
+
+            mScaledVertices = vertices.stream()
+                    .map(latLng -> convertAndScaleLatLng(latLng))
+                    .collect(Collectors.toList());
+        }
+
+        public List<LatLng> getVertices() {
+            return mVertices;
+        }
+
+        /**
+         * Check if the given point {@code p} is inside the polygon. This method counts the number
+         * of times the polygon winds around the point P, A.K.A "winding number". The point is
+         * outside only when this "winding number" is 0.
+         *
+         * If a point is on the edge of the polygon, it is also considered to be inside the polygon.
+         */
+        @Override
+        public boolean contains(LatLng latLng) {
+            Point p = convertAndScaleLatLng(latLng);
+
+            int n = mScaledVertices.size();
+            int windingNumber = 0;
+            for (int i = 0; i < n; i++) {
+                Point a = mScaledVertices.get(i);
+                Point b = mScaledVertices.get((i + 1) % n);
+
+                // CCW is counterclockwise
+                // CCW = ab x ap
+                // CCW > 0 -> ap is on the left side of ab
+                // CCW == 0 -> ap is on the same line of ab
+                // CCW < 0 -> ap is on the right side of ab
+                int ccw = sign(crossProduct(b.subtract(a), p.subtract(a)));
+
+                if (ccw == 0) {
+                    if (Math.min(a.x, b.x) <= p.x && p.x <= Math.max(a.x, b.x)
+                            && Math.min(a.y, b.y) <= p.y && p.y <= Math.max(a.y, b.y)) {
+                        return true;
+                    }
+                } else {
+                    if (sign(a.y - p.y) <= 0) {
+                        // upward crossing
+                        if (ccw > 0 && sign(b.y - p.y) > 0) {
+                            ++windingNumber;
+                        }
+                    } else {
+                        // downward crossing
+                        if (ccw < 0 && sign(b.y - p.y) <= 0) {
+                            --windingNumber;
+                        }
+                    }
+                }
+            }
+            return windingNumber != 0;
+        }
+
+        /**
+         * Move the given point {@code latLng} to the coordinate system with {@code mOrigin} as the
+         * origin and scale it. {@code mOrigin} is selected from the vertices of a polygon, it has
+         * the smallest longitude value among all of the polygon vertices.
+         *
+         * @param latLng the point need to be converted and scaled.
+         * @Return a {@link Point} object.
+         */
+        private Point convertAndScaleLatLng(LatLng latLng) {
+            double x = latLng.lat - mOrigin.lat;
+            double y = latLng.lng - mOrigin.lng;
+
+            // If the point is in different hemispheres(western/eastern) than the mOrigin, and the
+            // edge between them cross the 180th meridian, then its relative coordinates will be
+            // extended.
+            // For example, suppose the longitude of the mOrigin is -178, and the longitude of the
+            // point to be converted is 175, then the longitude after the conversion is -8.
+            // calculation: (-178 - 8) - (-178).
+            if (sign(mOrigin.lng) != 0 && sign(mOrigin.lng) != sign(latLng.lng)) {
+                double distCross0thMeridian = Math.abs(mOrigin.lng) + Math.abs(latLng.lng);
+                if (sign(distCross0thMeridian * 2 - 360) > 0) {
+                    y = sign(mOrigin.lng) * (360 - distCross0thMeridian);
+                }
+            }
+            return new Point(x * SCALE, y * SCALE);
+        }
+
+        private static double crossProduct(Point a, Point b) {
+            return a.x * b.y - a.y * b.x;
+        }
+
+        static final class Point {
+            public final double x;
+            public final double y;
+
+            Point(double x, double y) {
+                this.x = x;
+                this.y = y;
+            }
+
+            public Point subtract(Point p) {
+                return new Point(x - p.x, y - p.y);
+            }
+        }
+    }
+
+    /** The class represents a circle. */
+    public static class Circle implements Geometry {
+        private final LatLng mCenter;
+        private final double mRadiusMeter;
+
+        public Circle(LatLng center, double radiusMeter) {
+            this.mCenter = center;
+            this.mRadiusMeter = radiusMeter;
+        }
+
+        public LatLng getCenter() {
+            return mCenter;
+        }
+
+        public double getRadius() {
+            return mRadiusMeter;
+        }
+
+        @Override
+        public boolean contains(LatLng p) {
+            return mCenter.distance(p) <= mRadiusMeter;
+        }
+    }
+
+    /**
+     * Parse the geometries from the encoded string {@code str}. The string must follow the
+     * geometry encoding specified by {@link android.provider.Telephony.CellBroadcasts#GEOMETRIES}.
+     */
+    @NonNull
+    public static List<Geometry> parseGeometriesFromString(@NonNull String str) {
+        List<Geometry> geometries = new ArrayList<>();
+        for (String geometryStr : str.split("\\s*;\\s*")) {
+            String[] geoParameters = geometryStr.split("\\s*\\|\\s*");
+            switch (geoParameters[0]) {
+                case CIRCLE_SYMBOL:
+                    geometries.add(new Circle(parseLatLngFromString(geoParameters[1]),
+                            Double.parseDouble(geoParameters[2])));
+                    break;
+                case POLYGON_SYMBOL:
+                    List<LatLng> vertices = new ArrayList<>(geoParameters.length - 1);
+                    for (int i = 1; i < geoParameters.length; i++) {
+                        vertices.add(parseLatLngFromString(geoParameters[i]));
+                    }
+                    geometries.add(new Polygon(vertices));
+                    break;
+                default:
+                    Rlog.e(TAG, "Invalid geometry format " + geometryStr);
+            }
+        }
+        return geometries;
+    }
+
+    /**
+     * Encode a list of geometry objects to string. The encoding format is specified by
+     * {@link android.provider.Telephony.CellBroadcasts#GEOMETRIES}.
+     *
+     * @param geometries the list of geometry objects need to be encoded.
+     * @return the encoded string.
+     */
+    @NonNull
+    public static String encodeGeometriesToString(@NonNull List<Geometry> geometries) {
+        return geometries.stream()
+                .map(geometry -> encodeGeometryToString(geometry))
+                .filter(encodedStr -> !TextUtils.isEmpty(encodedStr))
+                .collect(Collectors.joining(";"));
+    }
+
+
+    /**
+     * Encode the geometry object to string. The encoding format is specified by
+     * {@link android.provider.Telephony.CellBroadcasts#GEOMETRIES}.
+     * @param geometry the geometry object need to be encoded.
+     * @return the encoded string.
+     */
+    @NonNull
+    private static String encodeGeometryToString(@NonNull Geometry geometry) {
+        StringBuilder sb = new StringBuilder();
+        if (geometry instanceof Polygon) {
+            sb.append(POLYGON_SYMBOL);
+            for (LatLng latLng : ((Polygon) geometry).getVertices()) {
+                sb.append("|");
+                sb.append(latLng.lat);
+                sb.append(",");
+                sb.append(latLng.lng);
+            }
+        } else if (geometry instanceof Circle) {
+            sb.append(CIRCLE_SYMBOL);
+            Circle circle = (Circle) geometry;
+
+            // Center
+            sb.append("|");
+            sb.append(circle.getCenter().lat);
+            sb.append(",");
+            sb.append(circle.getCenter().lng);
+
+            // Radius
+            sb.append("|");
+            sb.append(circle.getRadius());
+        } else {
+            Rlog.e(TAG, "Unsupported geometry object " + geometry);
+            return null;
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Parse {@link LatLng} from {@link String}. Latitude and longitude are separated by ",".
+     * Example: "13.56,-55.447".
+     *
+     * @param str encoded lat/lng string.
+     * @Return {@link LatLng} object.
+     */
+    @NonNull
+    public static LatLng parseLatLngFromString(@NonNull String str) {
+        String[] latLng = str.split("\\s*,\\s*");
+        return new LatLng(Double.parseDouble(latLng[0]), Double.parseDouble(latLng[1]));
+    }
+
+    /**
+     * @Return the sign of the given value {@code value} with the specified tolerance. Return 1
+     * means the sign is positive, -1 means negative, 0 means the value will be treated as 0.
+     */
+    public static int sign(double value) {
+        if (value > EPS) return 1;
+        if (value < -EPS) return -1;
+        return 0;
+    }
+}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index ca65736..e9c24cd 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -70,7 +70,6 @@
 import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -1170,15 +1169,16 @@
         updateVerboseLoggingEnabledFromService();
     }
 
-    private IWifiManager getIWifiManager() throws RemoteException {
+    private IWifiManager getIWifiManager() {
         if (mService == null) {
             synchronized (this) {
                 mService = IWifiManager.Stub.asInterface(
                         ServiceManager.getService(Context.WIFI_SERVICE));
-                if (mService == null) {
-                    throw new RemoteException("Wifi Service not running");
+                if (mService != null) {
+                    updateVerboseLoggingEnabledFromService();
+                } else {
+                    Log.e(TAG, "Wifi Service not running yet, ignoring WifiManager API call");
                 }
-                updateVerboseLoggingEnabledFromService();
             }
         }
         return mService;
@@ -1223,8 +1223,10 @@
     @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, ACCESS_WIFI_STATE})
     public List<WifiConfiguration> getConfiguredNetworks() {
         try {
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return Collections.emptyList();
             ParceledListSlice<WifiConfiguration> parceledList =
-                    getIWifiManager().getConfiguredNetworks(mContext.getOpPackageName());
+                    iWifiManager.getConfiguredNetworks(mContext.getOpPackageName());
             if (parceledList == null) {
                 return Collections.emptyList();
             }
@@ -1239,8 +1241,10 @@
     @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, ACCESS_WIFI_STATE, READ_WIFI_CREDENTIAL})
     public List<WifiConfiguration> getPrivilegedConfiguredNetworks() {
         try {
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return Collections.emptyList();
             ParceledListSlice<WifiConfiguration> parceledList =
-                    getIWifiManager().getPrivilegedConfiguredNetworks(mContext.getOpPackageName());
+                    iWifiManager.getPrivilegedConfiguredNetworks(mContext.getOpPackageName());
             if (parceledList == null) {
                 return Collections.emptyList();
             }
@@ -1271,14 +1275,16 @@
             @NonNull List<ScanResult> scanResults) {
         List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> configs = new ArrayList<>();
         try {
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return Collections.emptyList();
             Map<String, Map<Integer, List<ScanResult>>> results =
-                    getIWifiManager().getAllMatchingFqdnsForScanResults(
+                    iWifiManager.getAllMatchingFqdnsForScanResults(
                             scanResults);
             if (results.isEmpty()) {
                 return configs;
             }
             List<WifiConfiguration> wifiConfigurations =
-                    getIWifiManager().getWifiConfigsForPasspointProfiles(
+                    iWifiManager.getWifiConfigsForPasspointProfiles(
                             new ArrayList<>(results.keySet()));
             for (WifiConfiguration configuration : wifiConfigurations) {
                 Map<Integer, List<ScanResult>> scanResultsPerNetworkType = results.get(
@@ -1313,10 +1319,12 @@
     public Map<OsuProvider, List<ScanResult>> getMatchingOsuProviders(
             @Nullable List<ScanResult> scanResults) {
         if (scanResults == null) {
-            return new HashMap<>();
+            return Collections.emptyMap();
         }
         try {
-            return getIWifiManager().getMatchingOsuProviders(scanResults);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return Collections.emptyMap();
+            return iWifiManager.getMatchingOsuProviders(scanResults);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1343,7 +1351,9 @@
     public Map<OsuProvider, PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders(
             @NonNull Set<OsuProvider> osuProviders) {
         try {
-            return getIWifiManager().getMatchingPasspointConfigsForOsuProviders(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return Collections.emptyMap();
+            return iWifiManager.getMatchingPasspointConfigsForOsuProviders(
                     new ArrayList<>(osuProviders));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1428,7 +1438,9 @@
      */
     private int addOrUpdateNetwork(WifiConfiguration config) {
         try {
-            return getIWifiManager().addOrUpdateNetwork(config, mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return -1;
+            return iWifiManager.addOrUpdateNetwork(config, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1654,7 +1666,11 @@
         Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper();
         Binder binder = new Binder();
         try {
-            getIWifiManager().registerNetworkRequestMatchCallback(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.registerNetworkRequestMatchCallback(
                     binder, new NetworkRequestMatchCallbackProxy(looper, callback),
                     callback.hashCode());
         } catch (RemoteException e) {
@@ -1680,7 +1696,11 @@
         Log.v(TAG, "unregisterNetworkRequestMatchCallback: callback=" + callback);
 
         try {
-            getIWifiManager().unregisterNetworkRequestMatchCallback(callback.hashCode());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.unregisterNetworkRequestMatchCallback(callback.hashCode());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1714,7 +1734,9 @@
     public @NetworkSuggestionsStatusCode int addNetworkSuggestions(
             @NonNull List<WifiNetworkSuggestion> networkSuggestions) {
         try {
-            return getIWifiManager().addNetworkSuggestions(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL;
+            return iWifiManager.addNetworkSuggestions(
                     networkSuggestions, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1738,7 +1760,9 @@
     public @NetworkSuggestionsStatusCode int removeNetworkSuggestions(
             @NonNull List<WifiNetworkSuggestion> networkSuggestions) {
         try {
-            return getIWifiManager().removeNetworkSuggestions(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL;
+            return iWifiManager.removeNetworkSuggestions(
                     networkSuggestions, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1754,7 +1778,9 @@
     @RequiresPermission(ACCESS_WIFI_STATE)
     public @NonNull List<WifiNetworkSuggestion> getNetworkSuggestions() {
         try {
-            return mService.getNetworkSuggestions(mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return Collections.emptyList();
+            return iWifiManager.getNetworkSuggestions(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -1784,7 +1810,11 @@
      */
     public void addOrUpdatePasspointConfiguration(PasspointConfiguration config) {
         try {
-            if (!getIWifiManager().addOrUpdatePasspointConfiguration(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            if (!iWifiManager.addOrUpdatePasspointConfiguration(
                     config, mContext.getOpPackageName())) {
                 throw new IllegalArgumentException();
             }
@@ -1808,7 +1838,11 @@
     })
     public void removePasspointConfiguration(String fqdn) {
         try {
-            if (!getIWifiManager().removePasspointConfiguration(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            if (!iWifiManager.removePasspointConfiguration(
                     fqdn, mContext.getOpPackageName())) {
                 throw new IllegalArgumentException();
             }
@@ -1832,7 +1866,9 @@
     })
     public List<PasspointConfiguration> getPasspointConfigurations() {
         try {
-            return getIWifiManager().getPasspointConfigurations(mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) Collections.emptyList();
+            return iWifiManager.getPasspointConfigurations(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1852,7 +1888,11 @@
      */
     public void queryPasspointIcon(long bssid, String fileName) {
         try {
-            getIWifiManager().queryPasspointIcon(bssid, fileName);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.queryPasspointIcon(bssid, fileName);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1866,7 +1906,9 @@
      */
     public int matchProviderWithCurrentNetwork(String fqdn) {
         try {
-            return getIWifiManager().matchProviderWithCurrentNetwork(fqdn);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return -1;
+            return iWifiManager.matchProviderWithCurrentNetwork(fqdn);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1880,7 +1922,11 @@
      */
     public void deauthenticateNetwork(long holdoff, boolean ess) {
         try {
-            getIWifiManager().deauthenticateNetwork(holdoff, ess);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.deauthenticateNetwork(holdoff, ess);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1910,7 +1956,9 @@
     @Deprecated
     public boolean removeNetwork(int netId) {
         try {
-            return getIWifiManager().removeNetwork(netId, mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.removeNetwork(netId, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1965,7 +2013,9 @@
 
         boolean success;
         try {
-            success = getIWifiManager().enableNetwork(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            success = iWifiManager.enableNetwork(
                     netId, attemptConnect, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -2002,7 +2052,9 @@
     @Deprecated
     public boolean disableNetwork(int netId) {
         try {
-            return getIWifiManager().disableNetwork(netId, mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.disableNetwork(netId, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2025,7 +2077,9 @@
     @Deprecated
     public boolean disconnect() {
         try {
-            return getIWifiManager().disconnect(mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.disconnect(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2049,7 +2103,9 @@
     @Deprecated
     public boolean reconnect() {
         try {
-            return getIWifiManager().reconnect(mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.reconnect(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2073,7 +2129,9 @@
     @Deprecated
     public boolean reassociate() {
         try {
-            return getIWifiManager().reassociate(mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.reassociate(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2159,7 +2217,9 @@
 
     private long getSupportedFeatures() {
         try {
-            return getIWifiManager().getSupportedFeatures();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return 0L;
+            return iWifiManager.getSupportedFeatures();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2289,8 +2349,10 @@
      */
     public WifiActivityEnergyInfo getControllerActivityEnergyInfo() {
         try {
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return null;
             synchronized(this) {
-                return getIWifiManager().reportActivityInfo();
+                return iWifiManager.reportActivityInfo();
             }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -2329,8 +2391,10 @@
     @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
     public boolean startScan(WorkSource workSource) {
         try {
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
             String packageName = mContext.getOpPackageName();
-            return getIWifiManager().startScan(packageName);
+            return iWifiManager.startScan(packageName);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2359,7 +2423,9 @@
      */
     public WifiInfo getConnectionInfo() {
         try {
-            return getIWifiManager().getConnectionInfo(mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return null;
+            return iWifiManager.getConnectionInfo(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2373,7 +2439,9 @@
      */
     public List<ScanResult> getScanResults() {
         try {
-            return getIWifiManager().getScanResults(mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return Collections.emptyList();
+            return iWifiManager.getScanResults(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2392,7 +2460,9 @@
     @Deprecated
     public boolean isScanAlwaysAvailable() {
         try {
-            return getIWifiManager().isScanAlwaysAvailable();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.isScanAlwaysAvailable();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2423,7 +2493,11 @@
      */
     public void setCountryCode(@NonNull String country) {
         try {
-            getIWifiManager().setCountryCode(country);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.setCountryCode(country);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2438,7 +2512,9 @@
     @UnsupportedAppUsage
     public String getCountryCode() {
         try {
-            String country = getIWifiManager().getCountryCode();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return null;
+            String country = iWifiManager.getCountryCode();
             return country;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -2453,7 +2529,9 @@
     @UnsupportedAppUsage
     public boolean isDualBandSupported() {
         try {
-            return getIWifiManager().isDualBandSupported();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.isDualBandSupported();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2466,7 +2544,9 @@
      */
     public boolean isDualModeSupported() {
         try {
-            return getIWifiManager().needs5GHzToAnyApBandConversion();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.needs5GHzToAnyApBandConversion();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2479,7 +2559,9 @@
      */
     public DhcpInfo getDhcpInfo() {
         try {
-            return getIWifiManager().getDhcpInfo();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return null;
+            return iWifiManager.getDhcpInfo();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2506,7 +2588,9 @@
     @Deprecated
     public boolean setWifiEnabled(boolean enabled) {
         try {
-            return getIWifiManager().setWifiEnabled(mContext.getOpPackageName(), enabled);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.setWifiEnabled(mContext.getOpPackageName(), enabled);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2521,7 +2605,9 @@
      */
     public int getWifiState() {
         try {
-            return getIWifiManager().getWifiEnabledState();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return WIFI_STATE_UNKNOWN;
+            return iWifiManager.getWifiEnabledState();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2595,7 +2681,11 @@
      */
     public void updateInterfaceIpState(String ifaceName, int mode) {
         try {
-            getIWifiManager().updateInterfaceIpState(ifaceName, mode);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.updateInterfaceIpState(ifaceName, mode);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2613,7 +2703,9 @@
      */
     public boolean startSoftAp(@Nullable WifiConfiguration wifiConfig) {
         try {
-            return getIWifiManager().startSoftAp(wifiConfig);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.startSoftAp(wifiConfig);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2628,7 +2720,9 @@
      */
     public boolean stopSoftAp() {
         try {
-            return getIWifiManager().stopSoftAp();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.stopSoftAp();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2696,8 +2790,12 @@
             LocalOnlyHotspotCallbackProxy proxy =
                     new LocalOnlyHotspotCallbackProxy(this, looper, callback);
             try {
+                IWifiManager iWifiManager = getIWifiManager();
+                if (iWifiManager == null) {
+                    throw new RemoteException("Wifi service is not running");
+                }
                 String packageName = mContext.getOpPackageName();
-                int returnCode = getIWifiManager().startLocalOnlyHotspot(
+                int returnCode = iWifiManager.startLocalOnlyHotspot(
                         proxy.getMessenger(), new Binder(), packageName);
                 if (returnCode != LocalOnlyHotspotCallback.REQUEST_REGISTERED) {
                     // Send message to the proxy to make sure we call back on the correct thread
@@ -2749,7 +2847,11 @@
             }
             mLOHSCallbackProxy = null;
             try {
-                getIWifiManager().stopLocalOnlyHotspot();
+                IWifiManager iWifiManager = getIWifiManager();
+                if (iWifiManager == null) {
+                    throw new RemoteException("Wifi service is not running");
+                }
+                iWifiManager.stopLocalOnlyHotspot();
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -2779,7 +2881,11 @@
             Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper();
             mLOHSObserverProxy = new LocalOnlyHotspotObserverProxy(this, looper, observer);
             try {
-                getIWifiManager().startWatchLocalOnlyHotspot(
+                IWifiManager iWifiManager = getIWifiManager();
+                if (iWifiManager == null) {
+                    throw new RemoteException("Wifi service is not running");
+                }
+                iWifiManager.startWatchLocalOnlyHotspot(
                         mLOHSObserverProxy.getMessenger(), new Binder());
                 mLOHSObserverProxy.registered();
             } catch (RemoteException e) {
@@ -2803,7 +2909,11 @@
             }
             mLOHSObserverProxy = null;
             try {
-                getIWifiManager().stopWatchLocalOnlyHotspot();
+                IWifiManager iWifiManager = getIWifiManager();
+                if (iWifiManager == null) {
+                    throw new RemoteException("Wifi service is not running");
+                }
+                iWifiManager.stopWatchLocalOnlyHotspot();
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -2823,7 +2933,9 @@
     @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE)
     public int getWifiApState() {
         try {
-            return getIWifiManager().getWifiApEnabledState();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return WIFI_AP_STATE_FAILED;
+            return iWifiManager.getWifiApEnabledState();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2852,7 +2964,9 @@
     @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE)
     public WifiConfiguration getWifiApConfiguration() {
         try {
-            return getIWifiManager().getWifiApConfiguration();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return null;
+            return iWifiManager.getWifiApConfiguration();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2869,7 +2983,9 @@
     @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE)
     public boolean setWifiApConfiguration(WifiConfiguration wifiConfig) {
         try {
-            return getIWifiManager().setWifiApConfiguration(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.setWifiApConfiguration(
                     wifiConfig, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -2885,7 +3001,11 @@
     public void notifyUserOfApBandConversion() {
         Log.d(TAG, "apBand was converted, notify the user");
         try {
-            getIWifiManager().notifyUserOfApBandConversion(mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.notifyUserOfApBandConversion(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2913,7 +3033,11 @@
      */
     public void setTdlsEnabled(InetAddress remoteIPAddress, boolean enable) {
         try {
-            getIWifiManager().enableTdls(remoteIPAddress.getHostAddress(), enable);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.enableTdls(remoteIPAddress.getHostAddress(), enable);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2927,7 +3051,11 @@
      */
     public void setTdlsEnabledWithMacAddress(String remoteMacAddress, boolean enable) {
         try {
-            getIWifiManager().enableTdlsWithMacAddress(remoteMacAddress, enable);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.enableTdlsWithMacAddress(remoteMacAddress, enable);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3216,7 +3344,11 @@
         Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper();
         Binder binder = new Binder();
         try {
-            getIWifiManager().registerSoftApCallback(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.registerSoftApCallback(
                     binder, new SoftApCallbackProxy(looper, callback), callback.hashCode());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -3237,7 +3369,11 @@
         Log.v(TAG, "unregisterSoftApCallback: callback=" + callback);
 
         try {
-            getIWifiManager().unregisterSoftApCallback(callback.hashCode());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.unregisterSoftApCallback(callback.hashCode());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3807,7 +3943,11 @@
     public void disableEphemeralNetwork(String SSID) {
         if (SSID == null) throw new IllegalArgumentException("SSID cannot be null");
         try {
-            getIWifiManager().disableEphemeralNetwork(SSID, mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.disableEphemeralNetwork(SSID, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3851,7 +3991,9 @@
     @UnsupportedAppUsage
     private Messenger getWifiServiceMessenger() {
         try {
-            return getIWifiManager().getWifiServiceMessenger(mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return null;
+            return iWifiManager.getWifiServiceMessenger(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3911,10 +4053,14 @@
             synchronized (mBinder) {
                 if (mRefCounted ? (++mRefCount == 1) : (!mHeld)) {
                     try {
-                        getIWifiManager().acquireWifiLock(mBinder, mLockType, mTag, mWorkSource);
+                        IWifiManager iWifiManager = getIWifiManager();
+                        if (iWifiManager == null) {
+                            throw new RemoteException("Wifi service is not running");
+                        }
+                        iWifiManager.acquireWifiLock(mBinder, mLockType, mTag, mWorkSource);
                         synchronized (WifiManager.this) {
                             if (mActiveLockCount >= MAX_ACTIVE_LOCKS) {
-                                getIWifiManager().releaseWifiLock(mBinder);
+                                iWifiManager.releaseWifiLock(mBinder);
                                 throw new UnsupportedOperationException(
                                             "Exceeded maximum number of wifi locks");
                             }
@@ -3944,7 +4090,11 @@
             synchronized (mBinder) {
                 if (mRefCounted ? (--mRefCount == 0) : (mHeld)) {
                     try {
-                        getIWifiManager().releaseWifiLock(mBinder);
+                        IWifiManager iWifiManager = getIWifiManager();
+                        if (iWifiManager == null) {
+                            throw new RemoteException("Wifi service is not running");
+                        }
+                        iWifiManager.releaseWifiLock(mBinder);
                         synchronized (WifiManager.this) {
                             mActiveLockCount--;
                         }
@@ -4007,7 +4157,11 @@
                 }
                 if (changed && mHeld) {
                     try {
-                        getIWifiManager().updateWifiLockWorkSource(mBinder, mWorkSource);
+                        IWifiManager iWifiManager = getIWifiManager();
+                        if (iWifiManager == null) {
+                            throw new RemoteException("Wifi service is not running");
+                        }
+                        iWifiManager.updateWifiLockWorkSource(mBinder, mWorkSource);
                     } catch (RemoteException e) {
                         throw e.rethrowFromSystemServer();
                     }
@@ -4035,7 +4189,11 @@
             synchronized (mBinder) {
                 if (mHeld) {
                     try {
-                        getIWifiManager().releaseWifiLock(mBinder);
+                        IWifiManager iWifiManager = getIWifiManager();
+                        if (iWifiManager == null) {
+                            throw new RemoteException("Wifi service is not running");
+                        }
+                        iWifiManager.releaseWifiLock(mBinder);
                         synchronized (WifiManager.this) {
                             mActiveLockCount--;
                         }
@@ -4148,10 +4306,14 @@
             synchronized (mBinder) {
                 if (mRefCounted ? (++mRefCount == 1) : (!mHeld)) {
                     try {
-                        getIWifiManager().acquireMulticastLock(mBinder, mTag);
+                        IWifiManager iWifiManager = getIWifiManager();
+                        if (iWifiManager == null) {
+                            throw new RemoteException("Wifi service is not running");
+                        }
+                        iWifiManager.acquireMulticastLock(mBinder, mTag);
                         synchronized (WifiManager.this) {
                             if (mActiveLockCount >= MAX_ACTIVE_LOCKS) {
-                                getIWifiManager().releaseMulticastLock(mTag);
+                                iWifiManager.releaseMulticastLock(mTag);
                                 throw new UnsupportedOperationException(
                                         "Exceeded maximum number of wifi locks");
                             }
@@ -4193,7 +4355,11 @@
             synchronized (mBinder) {
                 if (mRefCounted ? (--mRefCount == 0) : (mHeld)) {
                     try {
-                        getIWifiManager().releaseMulticastLock(mTag);
+                        IWifiManager iWifiManager = getIWifiManager();
+                        if (iWifiManager == null) {
+                            throw new RemoteException("Wifi service is not running");
+                        }
+                        iWifiManager.releaseMulticastLock(mTag);
                         synchronized (WifiManager.this) {
                             mActiveLockCount--;
                         }
@@ -4270,7 +4436,9 @@
      */
     public boolean isMulticastEnabled() {
         try {
-            return getIWifiManager().isMulticastEnabled();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.isMulticastEnabled();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4283,7 +4451,9 @@
     @UnsupportedAppUsage
     public boolean initializeMulticastFiltering() {
         try {
-            getIWifiManager().initializeMulticastFiltering();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            iWifiManager.initializeMulticastFiltering();
             return true;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -4308,7 +4478,11 @@
     @UnsupportedAppUsage
     public void enableVerboseLogging (int verbose) {
         try {
-            getIWifiManager().enableVerboseLogging(verbose);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.enableVerboseLogging(verbose);
         } catch (Exception e) {
             //ignore any failure here
             Log.e(TAG, "enableVerboseLogging " + e.toString());
@@ -4323,7 +4497,9 @@
     @UnsupportedAppUsage
     public int getVerboseLoggingLevel() {
         try {
-            return getIWifiManager().getVerboseLoggingLevel();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return -1;
+            return iWifiManager.getVerboseLoggingLevel();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4336,7 +4512,11 @@
      */
     public void factoryReset() {
         try {
-            getIWifiManager().factoryReset(mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.factoryReset(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4350,7 +4530,9 @@
     @UnsupportedAppUsage
     public Network getCurrentNetwork() {
         try {
-            return getIWifiManager().getCurrentNetwork();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return null;
+            return iWifiManager.getCurrentNetwork();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4382,7 +4564,11 @@
      */
     public void enableWifiConnectivityManager(boolean enabled) {
         try {
-            getIWifiManager().enableWifiConnectivityManager(enabled);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.enableWifiConnectivityManager(enabled);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4394,7 +4580,9 @@
      */
     public byte[] retrieveBackupData() {
         try {
-            return getIWifiManager().retrieveBackupData();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return null;
+            return iWifiManager.retrieveBackupData();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4406,7 +4594,11 @@
      */
     public void restoreBackupData(byte[] data) {
         try {
-            getIWifiManager().restoreBackupData(data);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.restoreBackupData(data);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4422,7 +4614,11 @@
     @Deprecated
     public void restoreSupplicantBackupData(byte[] supplicantData, byte[] ipConfigData) {
         try {
-            getIWifiManager().restoreSupplicantBackupData(supplicantData, ipConfigData);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.restoreSupplicantBackupData(supplicantData, ipConfigData);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4451,7 +4647,11 @@
             throw new IllegalArgumentException("callback must not be null");
         }
         try {
-            getIWifiManager().startSubscriptionProvisioning(provider,
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.startSubscriptionProvisioning(provider,
                     new ProvisioningCallbackProxy(executor, callback));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -4565,7 +4765,11 @@
         Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper();
         Binder binder = new Binder();
         try {
-            getIWifiManager().registerTrafficStateCallback(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.registerTrafficStateCallback(
                     binder, new TrafficStateCallbackProxy(looper, callback), callback.hashCode());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -4585,7 +4789,11 @@
         Log.v(TAG, "unregisterTrafficStateCallback: callback=" + callback);
 
         try {
-            getIWifiManager().unregisterTrafficStateCallback(callback.hashCode());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.unregisterTrafficStateCallback(callback.hashCode());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4641,7 +4849,9 @@
     @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     public String[] getFactoryMacAddresses() {
         try {
-            return getIWifiManager().getFactoryMacAddresses();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return null;
+            return iWifiManager.getFactoryMacAddresses();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4710,7 +4920,11 @@
     @RequiresPermission(android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE)
     public void setDeviceMobilityState(@DeviceMobilityState int state) {
         try {
-            getIWifiManager().setDeviceMobilityState(state);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.setDeviceMobilityState(state);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4765,7 +4979,11 @@
             @NonNull EasyConnectStatusCallback callback) {
         Binder binder = new Binder();
         try {
-            getIWifiManager().startDppAsConfiguratorInitiator(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.startDppAsConfiguratorInitiator(
                     binder, enrolleeUri, selectedNetworkId, enrolleeNetworkRole,
                     new EasyConnectCallbackProxy(executor, callback));
         } catch (RemoteException e) {
@@ -4792,7 +5010,11 @@
             @NonNull EasyConnectStatusCallback callback) {
         Binder binder = new Binder();
         try {
-            getIWifiManager().startDppAsEnrolleeInitiator(binder, configuratorUri,
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.startDppAsEnrolleeInitiator(binder, configuratorUri,
                     new EasyConnectCallbackProxy(executor, callback));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -4813,8 +5035,12 @@
             android.Manifest.permission.NETWORK_SETUP_WIZARD})
     public void stopEasyConnectSession() {
         try {
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
             /* Request lower layers to stop/abort and clear resources */
-            getIWifiManager().stopDppSession();
+            iWifiManager.stopDppSession();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4913,7 +5139,11 @@
             Log.v(TAG, "addOnWifiUsabilityStatsListener: listener=" + listener);
         }
         try {
-            getIWifiManager().addOnWifiUsabilityStatsListener(new Binder(),
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.addOnWifiUsabilityStatsListener(new Binder(),
                     new IOnWifiUsabilityStatsListener.Stub() {
                         @Override
                         public void onWifiUsabilityStats(int seqNum, boolean isSameBssidAndFreq,
@@ -4950,7 +5180,11 @@
             Log.v(TAG, "removeOnWifiUsabilityStatsListener: listener=" + listener);
         }
         try {
-            getIWifiManager().removeOnWifiUsabilityStatsListener(listener.hashCode());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.removeOnWifiUsabilityStatsListener(listener.hashCode());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4973,7 +5207,11 @@
     @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE)
     public void updateWifiUsabilityScore(int seqNum, int score, int predictionHorizonSec) {
         try {
-            getIWifiManager().updateWifiUsabilityScore(seqNum, score, predictionHorizonSec);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.updateWifiUsabilityScore(seqNum, score, predictionHorizonSec);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }