notifyCarrierNetworkChange:TelephonyManager->CarrierService

Re-checkin of 7f8be9d89b7f294bf5e5d377908a5c74d2f4968f.

ORIGINAL CHANGES:

Per API review, move TelephonyManager.notifyCarrierNetworkChange() to
CarrierService.notifyCarrierNetworkChange(). Underlying telephony
implementation remains unchanged.

Also minor tweaks to CarrierService:
* Remove some unnecessary @hide
* Remove final qualifier from onBind() so that subclasses can handle
  new internal callers that want to bind to it.

ADDITIONAL CHANGES:

- Fixes stack so that a SecurityException is thrown when caller
  does not have MODIFY_PHONE_STATE or carrier privileges.

Bug: 21572049
Bug: 21630803
Bug: 21721768

Change-Id: Ie952651d2f15c370de713ed8abb6d9f6f07dd2b4
diff --git a/api/current.txt b/api/current.txt
index c9a8efb..ee1f14f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -28651,7 +28651,8 @@
 
   public abstract class CarrierService extends android.app.Service {
     ctor public CarrierService();
-    method public final android.os.IBinder onBind(android.content.Intent);
+    method public final void notifyCarrierNetworkChange(boolean);
+    method public android.os.IBinder onBind(android.content.Intent);
     method public abstract android.os.PersistableBundle onLoadConfig(android.service.carrier.CarrierIdentifier);
     field public static final java.lang.String BIND_SERVICE_INTERFACE = "android.service.carrier.BindService";
     field public static final java.lang.String CONFIG_SERVICE_INTERFACE = "android.service.carrier.ConfigService";
@@ -31215,7 +31216,6 @@
     method public boolean isVoiceCapable();
     method public boolean isWorldPhone();
     method public void listen(android.telephony.PhoneStateListener, int);
-    method public void notifyCarrierNetworkChange(boolean);
     method public java.lang.String sendEnvelopeWithStatus(java.lang.String);
     method public boolean setLine1NumberForDisplay(java.lang.String, java.lang.String);
     method public boolean setOperatorBrandOverride(java.lang.String);
diff --git a/api/system-current.txt b/api/system-current.txt
index 2f26891..aebc187 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -30684,7 +30684,8 @@
 
   public abstract class CarrierService extends android.app.Service {
     ctor public CarrierService();
-    method public final android.os.IBinder onBind(android.content.Intent);
+    method public final void notifyCarrierNetworkChange(boolean);
+    method public android.os.IBinder onBind(android.content.Intent);
     method public abstract android.os.PersistableBundle onLoadConfig(android.service.carrier.CarrierIdentifier);
     field public static final java.lang.String BIND_SERVICE_INTERFACE = "android.service.carrier.BindService";
     field public static final java.lang.String CONFIG_SERVICE_INTERFACE = "android.service.carrier.ConfigService";
@@ -33459,7 +33460,6 @@
     method public boolean isWorldPhone();
     method public void listen(android.telephony.PhoneStateListener, int);
     method public boolean needsOtaServiceProvisioning();
-    method public void notifyCarrierNetworkChange(boolean);
     method public java.lang.String sendEnvelopeWithStatus(java.lang.String);
     method public void setDataEnabled(boolean);
     method public void setDataEnabled(int, boolean);
diff --git a/core/java/android/service/carrier/CarrierService.java b/core/java/android/service/carrier/CarrierService.java
index 4a4a375..4d21939 100644
--- a/core/java/android/service/carrier/CarrierService.java
+++ b/core/java/android/service/carrier/CarrierService.java
@@ -14,10 +14,15 @@
 
 package android.service.carrier;
 
+import android.annotation.CallSuper;
 import android.app.Service;
 import android.content.Intent;
 import android.os.IBinder;
 import android.os.PersistableBundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import com.android.internal.telephony.ITelephonyRegistry;
 
 /**
  * A service that exposes carrier-specific functionality to the system.
@@ -45,10 +50,16 @@
     public static final String CONFIG_SERVICE_INTERFACE = "android.service.carrier.ConfigService";
     public static final String BIND_SERVICE_INTERFACE = "android.service.carrier.BindService";
 
+    private static ITelephonyRegistry sRegistry;
+
     private final ICarrierService.Stub mStubWrapper;
 
     public CarrierService() {
         mStubWrapper = new ICarrierServiceWrapper();
+        if (sRegistry == null) {
+            sRegistry = ITelephonyRegistry.Stub.asInterface(
+                    ServiceManager.getService("telephony.registry"));
+        }
     }
 
     /**
@@ -83,9 +94,39 @@
      */
     public abstract PersistableBundle onLoadConfig(CarrierIdentifier id);
 
-    /** @hide */
+    /**
+     * Informs the system of an intentional upcoming carrier network change by
+     * a carrier app. This call is optional and is only used to allow the
+     * system to provide alternative UI while telephony is performing an action
+     * that may result in intentional, temporary network lack of connectivity.
+     * <p>
+     * Based on the active parameter passed in, this method will either show or
+     * hide the alternative UI. There is no timeout associated with showing
+     * this UX, so a carrier app must be sure to call with active set to false
+     * sometime after calling with it set to true.
+     * <p>
+     * Requires Permission:
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+     * Or the calling app has carrier privileges.
+     *   @see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}
+     *
+     * @param active Whether the carrier network change is or shortly will be
+     *               active. Set this value to true to begin showing
+     *               alternative UI and false to stop.
+     */
+    public final void notifyCarrierNetworkChange(boolean active) {
+        try {
+            if (sRegistry != null) sRegistry.notifyCarrierNetworkChange(active);
+        } catch (RemoteException | NullPointerException ex) {}
+    }
+
+    /**
+     * If overriding this method, call through to the super method for any unknown actions.
+     * {@inheritDoc}
+     */
     @Override
-    public final IBinder onBind(Intent intent) {
+    @CallSuper
+    public IBinder onBind(Intent intent) {
         switch (intent.getAction()) {
             case CONFIG_SERVICE_INTERFACE:
             case BIND_SERVICE_INTERFACE:
@@ -98,11 +139,8 @@
     /**
      * A wrapper around ICarrierService that forwards calls to implementations of
      * {@link CarrierService}.
-     *
-     * @hide
      */
     private class ICarrierServiceWrapper extends ICarrierService.Stub {
-
         @Override
         public PersistableBundle getCarrierConfig(CarrierIdentifier id) {
             return CarrierService.this.onLoadConfig(id);
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index bc93268..01a4ec8 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -817,9 +817,8 @@
 
     @Override
     public void notifyCarrierNetworkChange(boolean active) {
-        if (!checkNotifyPermissionOrCarrierPrivilege("notifyCarrierNetworkChange()")) {
-            return;
-        }
+        enforceNotifyPermissionOrCarrierPrivilege("notifyCarrierNetworkChange()");
+
         if (VDBG) {
             log("notifyCarrierNetworkChange: active=" + active);
         }
@@ -1486,15 +1485,12 @@
                 android.Manifest.permission.READ_PRECISE_PHONE_STATE);
     }
 
-    private boolean checkNotifyPermissionOrCarrierPrivilege(String method) {
-        if  (checkNotifyPermission() || checkCarrierPrivilege()) {
-            return true;
+    private void enforceNotifyPermissionOrCarrierPrivilege(String method) {
+        if  (checkNotifyPermission()) {
+            return;
         }
 
-        String msg = "Modify Phone State or Carrier Privilege Permission Denial: " + method
-                + " from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid();
-        if (DBG) log(msg);
-        return false;
+        enforceCarrierPrivilege();
     }
 
     private boolean checkNotifyPermission(String method) {
@@ -1512,17 +1508,20 @@
                 == PackageManager.PERMISSION_GRANTED;
     }
 
-    private boolean checkCarrierPrivilege() {
+    private void enforceCarrierPrivilege() {
         TelephonyManager tm = TelephonyManager.getDefault();
         String[] pkgs = mContext.getPackageManager().getPackagesForUid(Binder.getCallingUid());
         for (String pkg : pkgs) {
             if (tm.checkCarrierPrivilegesForPackage(pkg) ==
                     TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
-                return true;
+                return;
             }
         }
 
-        return false;
+        String msg = "Carrier Privilege Permission Denial: from pid=" + Binder.getCallingPid()
+                + ", uid=" + Binder.getCallingUid();
+        if (DBG) log(msg);
+        throw new SecurityException(msg);
     }
 
     private void checkListenerPermission(int events) {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f51cb65..581a59e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2060,35 +2060,6 @@
     }
 
     /**
-     * Informs the system of an intentional upcoming carrier network change by
-     * a carrier app. This call is optional and is only used to allow the
-     * system to provide alternative UI while telephony is performing an action
-     * that may result in intentional, temporary network lack of connectivity.
-     * <p>
-     * Based on the active parameter passed in, this method will either show or
-     * hide the alternative UI. There is no timeout associated with showing
-     * this UX, so a carrier app must be sure to call with active set to false
-     * sometime after calling with it set to true.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges.
-     *   @see #hasCarrierPrivileges
-     *
-     * @param active Whether the carrier network change is or shortly will be
-     *               active. Set this value to true to begin showing
-     *               alternative UI and false to stop.
-     */
-    public void notifyCarrierNetworkChange(boolean active) {
-        try {
-            if (sRegistry != null)
-                sRegistry.notifyCarrierNetworkChange(active);
-        } catch (RemoteException ex) {
-        } catch (NullPointerException ex) {
-        }
-    }
-
-    /**
      * Returns the alphabetic identifier associated with the line 1 number.
      * Return null if it is unavailable.
      * <p>