diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java index 3d294de256ff3..6249a068f591d 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java @@ -425,6 +425,10 @@ public class ContextHubClientBroker extends IContextHubClient.Stub } } + /* package */ String getPackageName() { + return mPackage; + } + /** * Used to override the attribution tag with a newer value if a PendingIntent broker is * retrieved. @@ -645,6 +649,13 @@ public class ContextHubClientBroker extends IContextHubClient.Stub */ private void updateNanoAppAuthState( long nanoAppId, boolean hasPermissions, boolean gracePeriodExpired) { + updateNanoAppAuthState( + nanoAppId, hasPermissions, gracePeriodExpired, false /* forceDenied */); + } + + /* package */ void updateNanoAppAuthState( + long nanoAppId, boolean hasPermissions, boolean gracePeriodExpired, + boolean forceDenied) { int curAuthState; int newAuthState; synchronized (mMessageChannelNanoappIdMap) { @@ -655,7 +666,10 @@ public class ContextHubClientBroker extends IContextHubClient.Stub // GRANTED -> DENIED_GRACE_PERIOD only if permissions have been lost // DENIED_GRACE_PERIOD -> DENIED only if the grace period expires // DENIED/DENIED_GRACE_PERIOD -> GRANTED only if permissions are granted again - if (gracePeriodExpired) { + // any state -> DENIED if "forceDenied" is true + if (forceDenied) { + newAuthState = AUTHORIZATION_DENIED; + } else if (gracePeriodExpired) { if (curAuthState == AUTHORIZATION_DENIED_GRACE_PERIOD) { newAuthState = AUTHORIZATION_DENIED; } @@ -667,13 +681,12 @@ public class ContextHubClientBroker extends IContextHubClient.Stub } } - if (newAuthState == AUTHORIZATION_GRANTED) { + if (newAuthState != AUTHORIZATION_DENIED_GRACE_PERIOD) { AuthStateDenialTimer timer = mNappToAuthTimerMap.remove(nanoAppId); if (timer != null) { timer.cancel(); } - } else if (curAuthState == AUTHORIZATION_GRANTED - && newAuthState == AUTHORIZATION_DENIED_GRACE_PERIOD) { + } else if (curAuthState == AUTHORIZATION_GRANTED) { AuthStateDenialTimer timer = new AuthStateDenialTimer(this, nanoAppId, Looper.getMainLooper()); mNappToAuthTimerMap.put(nanoAppId, timer); diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientManager.java index bc3f4b1db46db..e3522f6d487c1 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubClientManager.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientManager.java @@ -318,6 +318,21 @@ import java.util.function.Consumer; forEachClientOfHub(contextHubId, client -> client.onNanoAppAborted(nanoAppId, abortCode)); } + /** + * Runs a command for each client that is attached to a hub with the given ID. + * + * @param contextHubId the ID of the hub + * @param callback the command to invoke for the client + */ + /* package */ void forEachClientOfHub( + int contextHubId, Consumer callback) { + for (ContextHubClientBroker broker : mHostEndPointIdToClientMap.values()) { + if (broker.getAttachedContextHubId() == contextHubId) { + callback.accept(broker); + } + } + } + /** * Returns an available host endpoint ID. * @@ -357,20 +372,6 @@ import java.util.function.Consumer; message, nanoappPermissions, messagePermissions)); } - /** - * Runs a command for each client that is attached to a hub with the given ID. - * - * @param contextHubId the ID of the hub - * @param callback the command to invoke for the client - */ - private void forEachClientOfHub(int contextHubId, Consumer callback) { - for (ContextHubClientBroker broker : mHostEndPointIdToClientMap.values()) { - if (broker.getAttachedContextHubId() == contextHubId) { - callback.accept(broker); - } - } - } - /** * Retrieves a ContextHubClientBroker object with a matching PendingIntent and Context Hub ID. * diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java index 40986fcf03cb8..0737db7b83587 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java @@ -50,6 +50,8 @@ import android.net.wifi.WifiManager; import android.os.Binder; import android.os.RemoteCallbackList; import android.os.RemoteException; +import android.os.ResultReceiver; +import android.os.ShellCallback; import android.os.UserHandle; import android.provider.Settings; import android.util.Log; @@ -365,6 +367,12 @@ public class ContextHubService extends IContextHubService.Stub { return wrapper; } + @Override + public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, + String[] args, ShellCallback callback, ResultReceiver result) { + new ContextHubShellCommand(mContext, this).exec(this, in, out, err, args, callback, result); + } + @Override public int registerCallback(IContextHubCallback callback) throws RemoteException { checkPermissions(); @@ -933,6 +941,16 @@ public class ContextHubService extends IContextHubService.Stub { // dump eventLog } + /* package */ void denyClientAuthState(int contextHubId, String packageName, long nanoAppId) { + mClientManager.forEachClientOfHub(contextHubId, client -> { + if (client.getPackageName().equals(packageName)) { + client.updateNanoAppAuthState( + nanoAppId, false /* hasPermissions */, false /* gracePeriodExpired */, + true /* forceDenied */); + } + }); + } + private void dump(ProtoOutputStream proto) { mContextHubIdToInfoMap.values().forEach(hubInfo -> { long token = proto.start(ContextHubServiceProto.CONTEXT_HUB_INFO); diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubShellCommand.java b/services/core/java/com/android/server/location/contexthub/ContextHubShellCommand.java new file mode 100644 index 0000000000000..5ec85e661b982 --- /dev/null +++ b/services/core/java/com/android/server/location/contexthub/ContextHubShellCommand.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2021 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.location.contexthub; + +import android.content.Context; +import android.os.ShellCommand; + +import java.io.PrintWriter; + +/** + * ShellCommands for ContextHubService. + * + * Use with {@code adb shell cmd contexthub ...}. + * + * @hide + */ +public class ContextHubShellCommand extends ShellCommand { + + // Internal service impl -- must perform security checks before touching. + private final ContextHubService mInternal; + + public ContextHubShellCommand(Context context, ContextHubService service) { + mInternal = service; + + context.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_CONTEXT_HUB, "ContextHubShellCommand"); + } + + @Override + public int onCommand(String cmd) { + if ("deny".equals(cmd)) { + return runDisableAuth(); + } + + return handleDefaultCommands(cmd); + } + + private int runDisableAuth() { + int contextHubId = Integer.decode(getNextArgRequired()); + String packageName = getNextArgRequired(); + long nanoAppId = Long.decode(getNextArgRequired()); + + mInternal.denyClientAuthState(contextHubId, packageName, nanoAppId); + return 0; + } + + @Override + public void onHelp() { + PrintWriter pw = getOutPrintWriter(); + pw.println("ContextHub commands:"); + pw.println(" help"); + pw.println(" Print this help text."); + pw.println(" deny [contextHubId] [packageName] [nanoAppId]"); + pw.println(" Immediately transitions the package's authentication state to denied so"); + pw.println(" can no longer communciate with the nanoapp."); + } +}