Refactor tether state change callback/broadcast for redactions

Refactor tether state change callback/broadcast to allow for redactions
based on the callback cookie of the recipient. This CL is a no-op and
will be used in a future CL to redact the SoftApConfiguration in
TetheringInterface.

Bug: 216524590
Test: atest TetheringTest CtsTetheringTests
Change-Id: I3684929e6ba832f4da353d48137967739ca558d6
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index df255f3..61833c2 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -216,10 +216,10 @@
      * Cookie added when registering {@link android.net.TetheringManager.TetheringEventCallback}.
      */
     private static class CallbackCookie {
-        public final boolean hasListClientsPermission;
+        public final boolean hasSystemPrivilege;
 
-        private CallbackCookie(boolean hasListClientsPermission) {
-            this.hasListClientsPermission = hasListClientsPermission;
+        private CallbackCookie(boolean hasSystemPrivilege) {
+            this.hasSystemPrivilege = hasSystemPrivilege;
         }
     }
 
@@ -263,7 +263,6 @@
     private boolean mRndisEnabled;       // track the RNDIS function enabled state
     private boolean mNcmEnabled;         // track the NCM function enabled state
     private Network mTetherUpstream;
-    private TetherStatesParcel mTetherStatesParcel;
     private boolean mDataSaverEnabled = false;
     private String mWifiP2pTetherInterface = null;
     private int mOffloadStatus = TETHER_HARDWARE_OFFLOAD_STOPPED;
@@ -1091,16 +1090,44 @@
     }
 
     // TODO: Figure out how to update for local hotspot mode interfaces.
-    private void sendTetherStateChangedBroadcast() {
+    private void notifyTetherStatesChanged() {
         if (!isTetheringSupported()) return;
 
+        sendTetherStatesChangedCallback();
+        sendTetherStatesChangedBroadcast();
+
+        int downstreamTypesMask = DOWNSTREAM_NONE;
+        for (int i = 0; i < mTetherStates.size(); i++) {
+            final TetherState tetherState = mTetherStates.valueAt(i);
+            final int type = tetherState.ipServer.interfaceType();
+            if (tetherState.lastState != IpServer.STATE_TETHERED) continue;
+            switch (type) {
+                case TETHERING_USB:
+                case TETHERING_WIFI:
+                case TETHERING_BLUETOOTH:
+                    downstreamTypesMask |= (1 << type);
+                    break;
+                default:
+                    // Do nothing.
+                    break;
+            }
+        }
+        mNotificationUpdater.onDownstreamChanged(downstreamTypesMask);
+    }
+
+    /**
+     * Builds a TetherStatesParcel for the specified CallbackCookie.
+     *
+     * @param cookie CallbackCookie of the receiving app.
+     * @return TetherStatesParcel with information redacted for the specified cookie.
+     */
+    private TetherStatesParcel buildTetherStatesParcel(CallbackCookie cookie) {
         final ArrayList<TetheringInterface> available = new ArrayList<>();
         final ArrayList<TetheringInterface> tethered = new ArrayList<>();
         final ArrayList<TetheringInterface> localOnly = new ArrayList<>();
         final ArrayList<TetheringInterface> errored = new ArrayList<>();
         final ArrayList<Integer> lastErrors = new ArrayList<>();
 
-        int downstreamTypesMask = DOWNSTREAM_NONE;
         for (int i = 0; i < mTetherStates.size(); i++) {
             final TetherState tetherState = mTetherStates.valueAt(i);
             final int type = tetherState.ipServer.interfaceType();
@@ -1118,41 +1145,16 @@
                     case TETHERING_USB:
                     case TETHERING_WIFI:
                     case TETHERING_BLUETOOTH:
-                        downstreamTypesMask |= (1 << type);
                         break;
                     default:
                         // Do nothing.
+                        break;
                 }
                 tethered.add(tetheringIface);
             }
         }
 
-        mTetherStatesParcel = buildTetherStatesParcel(available, localOnly, tethered, errored,
-                lastErrors);
-        reportTetherStateChanged(mTetherStatesParcel);
-
-        mContext.sendStickyBroadcastAsUser(buildStateChangeIntent(available, localOnly, tethered,
-                errored), UserHandle.ALL);
-        if (DBG) {
-            Log.d(TAG, String.format(
-                    "reportTetherStateChanged %s=[%s] %s=[%s] %s=[%s] %s=[%s]",
-                    "avail", TextUtils.join(",", available),
-                    "local_only", TextUtils.join(",", localOnly),
-                    "tether", TextUtils.join(",", tethered),
-                    "error", TextUtils.join(",", errored)));
-        }
-
-        mNotificationUpdater.onDownstreamChanged(downstreamTypesMask);
-    }
-
-    private TetherStatesParcel buildTetherStatesParcel(
-            final ArrayList<TetheringInterface> available,
-            final ArrayList<TetheringInterface> localOnly,
-            final ArrayList<TetheringInterface> tethered,
-            final ArrayList<TetheringInterface> errored,
-            final ArrayList<Integer> lastErrors) {
         final TetherStatesParcel parcel = new TetherStatesParcel();
-
         parcel.availableList = available.toArray(new TetheringInterface[0]);
         parcel.tetheredList = tethered.toArray(new TetheringInterface[0]);
         parcel.localOnlyList = localOnly.toArray(new TetheringInterface[0]);
@@ -1161,23 +1163,23 @@
         for (int i = 0; i < lastErrors.size(); i++) {
             parcel.lastErrorList[i] = lastErrors.get(i);
         }
-
         return parcel;
     }
 
-    private Intent buildStateChangeIntent(final ArrayList<TetheringInterface> available,
-            final ArrayList<TetheringInterface> localOnly,
-            final ArrayList<TetheringInterface> tethered,
-            final ArrayList<TetheringInterface> errored) {
+    private void sendTetherStatesChangedBroadcast() {
         final Intent bcast = new Intent(ACTION_TETHER_STATE_CHANGED);
         bcast.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
 
-        bcast.putStringArrayListExtra(EXTRA_AVAILABLE_TETHER, toIfaces(available));
-        bcast.putStringArrayListExtra(EXTRA_ACTIVE_LOCAL_ONLY, toIfaces(localOnly));
-        bcast.putStringArrayListExtra(EXTRA_ACTIVE_TETHER, toIfaces(tethered));
-        bcast.putStringArrayListExtra(EXTRA_ERRORED_TETHER, toIfaces(errored));
-
-        return bcast;
+        TetherStatesParcel parcel = buildTetherStatesParcel(null);
+        bcast.putStringArrayListExtra(
+                EXTRA_AVAILABLE_TETHER, toIfaces(Arrays.asList(parcel.availableList)));
+        bcast.putStringArrayListExtra(
+                EXTRA_ACTIVE_LOCAL_ONLY, toIfaces(Arrays.asList(parcel.localOnlyList)));
+        bcast.putStringArrayListExtra(
+                EXTRA_ACTIVE_TETHER, toIfaces(Arrays.asList(parcel.tetheredList)));
+        bcast.putStringArrayListExtra(
+                EXTRA_ERRORED_TETHER, toIfaces(Arrays.asList(parcel.erroredIfaceList)));
+        mContext.sendStickyBroadcastAsUser(bcast, UserHandle.ALL);
     }
 
     private class StateReceiver extends BroadcastReceiver {
@@ -2391,19 +2393,18 @@
 
     /** Register tethering event callback */
     void registerTetheringEventCallback(ITetheringEventCallback callback) {
-        final boolean hasListPermission =
-                hasCallingPermission(NETWORK_SETTINGS)
-                        || hasCallingPermission(PERMISSION_MAINLINE_NETWORK_STACK)
-                        || hasCallingPermission(NETWORK_STACK);
+        final boolean hasSystemPrivilege = hasCallingPermission(NETWORK_SETTINGS)
+                || hasCallingPermission(PERMISSION_MAINLINE_NETWORK_STACK)
+                || hasCallingPermission(NETWORK_STACK);
         mHandler.post(() -> {
-            mTetheringEventCallbacks.register(callback, new CallbackCookie(hasListPermission));
+            CallbackCookie cookie = new CallbackCookie(hasSystemPrivilege);
+            mTetheringEventCallbacks.register(callback, cookie);
             final TetheringCallbackStartedParcel parcel = new TetheringCallbackStartedParcel();
             parcel.supportedTypes = mSupportedTypeBitmap;
             parcel.upstreamNetwork = mTetherUpstream;
             parcel.config = mConfig.toStableParcelable();
-            parcel.states =
-                    mTetherStatesParcel != null ? mTetherStatesParcel : emptyTetherStatesParcel();
-            parcel.tetheredClients = hasListPermission
+            parcel.states = buildTetherStatesParcel(cookie);
+            parcel.tetheredClients = hasSystemPrivilege
                     ? mConnectedClientsTracker.getLastTetheredClients()
                     : Collections.emptyList();
             parcel.offloadStatus = mOffloadStatus;
@@ -2415,17 +2416,6 @@
         });
     }
 
-    private TetherStatesParcel emptyTetherStatesParcel() {
-        final TetherStatesParcel parcel = new TetherStatesParcel();
-        parcel.availableList = new TetheringInterface[0];
-        parcel.tetheredList = new TetheringInterface[0];
-        parcel.localOnlyList = new TetheringInterface[0];
-        parcel.erroredIfaceList = new TetheringInterface[0];
-        parcel.lastErrorList = new int[0];
-
-        return parcel;
-    }
-
     private boolean hasCallingPermission(@NonNull String permission) {
         return mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED;
     }
@@ -2484,12 +2474,14 @@
         }
     }
 
-    private void reportTetherStateChanged(TetherStatesParcel states) {
+    private void sendTetherStatesChangedCallback() {
         final int length = mTetheringEventCallbacks.beginBroadcast();
         try {
             for (int i = 0; i < length; i++) {
                 try {
-                    mTetheringEventCallbacks.getBroadcastItem(i).onTetherStatesChanged(states);
+                    TetherStatesParcel parcel = buildTetherStatesParcel(
+                            (CallbackCookie) mTetheringEventCallbacks.getBroadcastCookie(i));
+                    mTetheringEventCallbacks.getBroadcastItem(i).onTetherStatesChanged(parcel);
                 } catch (RemoteException e) {
                     // Not really very much to do here.
                 }
@@ -2497,6 +2489,18 @@
         } finally {
             mTetheringEventCallbacks.finishBroadcast();
         }
+
+        if (DBG) {
+            // Use a CallbackCookie with system privilege so nothing is redacted.
+            TetherStatesParcel parcel =
+                    buildTetherStatesParcel(new CallbackCookie(true /* hasSystemPrivilege */));
+            Log.d(TAG, String.format(
+                    "sendTetherStatesChangedCallback %s=[%s] %s=[%s] %s=[%s] %s=[%s]",
+                    "avail", TextUtils.join(",", Arrays.asList(parcel.availableList)),
+                    "local_only", TextUtils.join(",", Arrays.asList(parcel.localOnlyList)),
+                    "tether", TextUtils.join(",", Arrays.asList(parcel.tetheredList)),
+                    "error", TextUtils.join(",", Arrays.asList(parcel.erroredIfaceList))));
+        }
     }
 
     private void reportTetherClientsChanged(List<TetheredClient> clients) {
@@ -2506,7 +2510,7 @@
                 try {
                     final CallbackCookie cookie =
                             (CallbackCookie) mTetheringEventCallbacks.getBroadcastCookie(i);
-                    if (!cookie.hasListClientsPermission) continue;
+                    if (!cookie.hasSystemPrivilege) continue;
                     mTetheringEventCallbacks.getBroadcastItem(i).onTetherClientsChanged(clients);
                 } catch (RemoteException e) {
                     // Not really very much to do here.
@@ -2741,7 +2745,7 @@
                     return;
             }
             mTetherMainSM.sendMessage(which, state, 0, who);
-            sendTetherStateChangedBroadcast();
+            notifyTetherStatesChanged();
         }
 
         @Override