Merge "Initial implementation of Intelligence Service Shell commands."

This commit is contained in:
TreeHugger Robot
2018-12-05 01:36:10 +00:00
committed by Android (Google) Code Review
9 changed files with 373 additions and 40 deletions

View File

@@ -108,6 +108,7 @@ package android {
field public static final java.lang.String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
field public static final java.lang.String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
field public static final java.lang.String MANAGE_ROLE_HOLDERS = "android.permission.MANAGE_ROLE_HOLDERS";
field public static final java.lang.String MANAGE_SMART_SUGGESTIONS = "android.permission.MANAGE_SMART_SUGGESTIONS";
field public static final java.lang.String MANAGE_SOUND_TRIGGER = "android.permission.MANAGE_SOUND_TRIGGER";
field public static final java.lang.String MANAGE_SUBSCRIPTION_PLANS = "android.permission.MANAGE_SUBSCRIPTION_PLANS";
field public static final java.lang.String MANAGE_USB = "android.permission.MANAGE_USB";

View File

@@ -4170,6 +4170,11 @@
<permission android:name="android.permission.MANAGE_AUTO_FILL"
android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to manage the smart suggestions service.
@hide <p>Not for use by third-party applications.</p> -->
<permission android:name="android.permission.MANAGE_SMART_SUGGESTIONS"
android:protectionLevel="signature" />
<!-- Allows an app to set the theme overlay in /vendor/overlay
being used.
@hide <p>Not for use by third-party applications.</p> -->

View File

@@ -130,6 +130,7 @@
<uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
<uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
<uses-permission android:name="android.permission.MANAGE_AUTO_FILL" />
<uses-permission android:name="android.permission.MANAGE_SMART_SUGGESTIONS" />
<uses-permission android:name="android.permission.NETWORK_SETTINGS" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.SET_TIME" />

View File

@@ -175,10 +175,6 @@ public final class AutofillManagerService
}
};
// TODO(b/117779333): move to superclass / create super-class for ShellCommand
@GuardedBy("mLock")
private boolean mAllowInstantService;
/**
* Supported modes for Augmented Autofill Smart Suggestions.
*/
@@ -271,6 +267,11 @@ public final class AutofillManagerService
addCompatibilityModeRequestsLocked(service, userId);
}
@Override // from AbstractMasterSystemService
protected void enforceCallingPermissionForManagement() {
getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
}
@Override // from SystemService
public void onStart() {
publishBinderService(AUTOFILL_MANAGER_SERVICE, new AutoFillManagerServiceStub());
@@ -290,7 +291,7 @@ public final class AutofillManagerService
// Called by Shell command.
void destroySessions(@UserIdInt int userId, IResultReceiver receiver) {
Slog.i(TAG, "destroySessions() for userId " + userId);
getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
enforceCallingPermissionForManagement();
synchronized (mLock) {
if (userId != UserHandle.USER_ALL) {
@@ -313,7 +314,7 @@ public final class AutofillManagerService
// Called by Shell command.
void listSessions(int userId, IResultReceiver receiver) {
Slog.i(TAG, "listSessions() for userId " + userId);
getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
enforceCallingPermissionForManagement();
final Bundle resultData = new Bundle();
final ArrayList<String> sessions = new ArrayList<>();
@@ -340,7 +341,7 @@ public final class AutofillManagerService
// Called by Shell command.
void reset() {
Slog.i(TAG, "reset()");
getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
enforceCallingPermissionForManagement();
synchronized (mLock) {
visitServicesLocked((s) -> s.destroyLocked());
@@ -351,7 +352,7 @@ public final class AutofillManagerService
// Called by Shell command.
void setLogLevel(int level) {
Slog.i(TAG, "setLogLevel(): " + level);
getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
enforceCallingPermissionForManagement();
final long token = Binder.clearCallingIdentity();
try {
@@ -388,7 +389,7 @@ public final class AutofillManagerService
// Called by Shell command.
int getLogLevel() {
getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
enforceCallingPermissionForManagement();
synchronized (mLock) {
if (sVerbose) return AutofillManager.FLAG_ADD_CLIENT_VERBOSE;
@@ -399,7 +400,7 @@ public final class AutofillManagerService
// Called by Shell command.
int getMaxPartitions() {
getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
enforceCallingPermissionForManagement();
synchronized (mLock) {
return sPartitionMaxCount;
@@ -408,8 +409,8 @@ public final class AutofillManagerService
// Called by Shell command.
void setMaxPartitions(int max) {
getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
Slog.i(TAG, "setMaxPartitions(): " + max);
enforceCallingPermissionForManagement();
final long token = Binder.clearCallingIdentity();
try {
@@ -433,7 +434,7 @@ public final class AutofillManagerService
// Called by Shell command.
int getMaxVisibleDatasets() {
getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
enforceCallingPermissionForManagement();
synchronized (sLock) {
return sVisibleDatasetsMaxCount;
@@ -442,8 +443,8 @@ public final class AutofillManagerService
// Called by Shell command.
void setMaxVisibleDatasets(int max) {
getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
Slog.i(TAG, "setMaxVisibleDatasets(): " + max);
enforceCallingPermissionForManagement();
final long token = Binder.clearCallingIdentity();
try {
@@ -480,7 +481,7 @@ public final class AutofillManagerService
// Called by Shell command.
void getScore(@Nullable String algorithmName, @NonNull String value1,
@NonNull String value2, @NonNull RemoteCallback callback) {
getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
enforceCallingPermissionForManagement();
final FieldClassificationStrategy strategy =
new FieldClassificationStrategy(getContext(), UserHandle.USER_CURRENT);
@@ -491,33 +492,16 @@ public final class AutofillManagerService
// Called by Shell command.
Boolean getFullScreenMode() {
getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
enforceCallingPermissionForManagement();
return sFullScreenMode;
}
// Called by Shell command.
void setFullScreenMode(@Nullable Boolean mode) {
getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
enforceCallingPermissionForManagement();
sFullScreenMode = mode;
}
// Called by Shell command.
boolean getAllowInstantService() {
getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
synchronized (mLock) {
return mAllowInstantService;
}
}
// Called by Shell command.
void setAllowInstantService(boolean mode) {
getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
Slog.i(TAG, "setAllowInstantService(): " + mode);
synchronized (mLock) {
mAllowInstantService = mode;
}
}
private void setLoggingLevelsLocked(boolean debug, boolean verbose) {
com.android.server.autofill.Helper.sDebug = debug;
android.view.autofill.Helper.sDebug = debug;
@@ -1218,7 +1202,6 @@ public final class AutofillManagerService
mAutofillCompatState.dump(prefix, pw);
pw.print("from settings: ");
pw.println(getWhitelistedCompatModePackagesFromSettings());
pw.print("Allow instant service: "); pw.println(mAllowInstantService);
if (mSupportedSmartSuggestionModes != 0) {
pw.print("Smart Suggestion modes: ");
pw.println(smartSuggestionFlagsToString(mSupportedSmartSuggestionModes));

View File

@@ -92,6 +92,12 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
*/
public boolean debug = false;
/**
* Whether the service is allowed to bind to an instant-app.
*/
@GuardedBy("mLock")
protected boolean mAllowInstantService;
/**
* Users disabled due to {@link UserManager} restrictions, or {@code null} if the service cannot
* be disabled through {@link UserManager}.
@@ -175,6 +181,47 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
}
}
/**
* Gets whether the service is allowed to bind to an instant-app.
*
* <p>Typically called by {@code ShellCommand} during CTS tests.
*
* @throws SecurityException if caller is not allowed to manage this service's settings.
*/
public final boolean getAllowInstantService() {
enforceCallingPermissionForManagement();
synchronized (mLock) {
return mAllowInstantService;
}
}
/**
* Sets whether the service is allowed to bind to an instant-app.
*
* <p>Typically called by {@code ShellCommand} during CTS tests.
*
* @throws SecurityException if caller is not allowed to manage this service's settings.
*/
public final void setAllowInstantService(boolean mode) {
Slog.i(mTag, "setAllowInstantService(): " + mode);
enforceCallingPermissionForManagement();
synchronized (mLock) {
mAllowInstantService = mode;
}
}
/**
* Asserts that the caller has permissions to manage this service.
*
* <p>Typically called by {@code ShellCommand} implementations.
*
* @throws UnsupportedOperationException if subclass doesn't override it.
* @throws SecurityException if caller is not allowed to manage this service's settings.
*/
protected void enforceCallingPermissionForManagement() {
throw new UnsupportedOperationException("Not implemented by " + getClass());
}
/**
* Creates a new service that will be added to the cache.
*
@@ -362,6 +409,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
pw.print(prefix); pw.print("Debug: "); pw.print(realDebug);
pw.print(" Verbose: "); pw.println(realVerbose);
pw.print(prefix); pw.print("Disabled users: "); pw.println(mDisabledUsers);
pw.print(prefix); pw.print("Allow instant service: "); pw.println(mAllowInstantService);
pw.print(prefix); pw.print("Settings property: "); pw.println(
getServiceSettingsProperty());
pw.print(prefix); pw.print("Cached services: ");

View File

@@ -146,7 +146,7 @@ final class ContentCaptureSession implements RemoteIntelligenceServiceCallbacks
}
@Override // from RemoteScreenObservationServiceCallbacks
public void onServiceDied(AbstractRemoteService service) {
public void onServiceDied(AbstractRemoteService<?> service) {
// TODO(b/111276913): implement (remove session from PerUserSession?)
if (mService.isDebug()) {
Slog.d(TAG, "onServiceDied() for " + mId);
@@ -176,6 +176,10 @@ final class ContentCaptureSession implements RemoteIntelligenceServiceCallbacks
pw.println(mAutofillCallback != null);
}
String toShortString() {
return mId.getValue() + ":" + mActivityToken;
}
@Override
public String toString() {
return "ContentCaptureSession[id=" + mId.getValue() + ", act=" + mActivityToken + "]";

View File

@@ -16,6 +16,7 @@
package com.android.server.intelligence;
import static android.Manifest.permission.MANAGE_SMART_SUGGESTIONS;
import static android.content.Context.CONTENT_CAPTURE_MANAGER_SERVICE;
import android.annotation.NonNull;
@@ -26,8 +27,13 @@ import android.content.ComponentName;
import android.content.Context;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.UserHandle;
import android.os.UserManager;
import android.service.intelligence.InteractionSessionId;
import android.util.Slog;
import android.view.autofill.AutofillId;
import android.view.autofill.IAutoFillManagerClient;
import android.view.intelligence.ContentCaptureEvent;
@@ -42,6 +48,7 @@ import com.android.server.LocalServices;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
/**
@@ -56,6 +63,8 @@ public final class IntelligenceManagerService extends
private static final String TAG = "IntelligenceManagerService";
static final String RECEIVER_BUNDLE_EXTRA_SESSIONS = "sessions";
@GuardedBy("mLock")
private ActivityManagerInternal mAm;
@@ -90,6 +99,61 @@ public final class IntelligenceManagerService extends
service.destroyLocked();
}
@Override // from AbstractMasterSystemService
protected void enforceCallingPermissionForManagement() {
getContext().enforceCallingPermission(MANAGE_SMART_SUGGESTIONS, TAG);
}
// Called by Shell command.
void destroySessions(@UserIdInt int userId, @NonNull IResultReceiver receiver) {
Slog.i(TAG, "destroySessions() for userId " + userId);
enforceCallingPermissionForManagement();
synchronized (mLock) {
if (userId != UserHandle.USER_ALL) {
final IntelligencePerUserService service = peekServiceForUserLocked(userId);
if (service != null) {
service.destroySessionsLocked();
}
} else {
visitServicesLocked((s) -> s.destroySessionsLocked());
}
}
try {
receiver.send(0, new Bundle());
} catch (RemoteException e) {
// Just ignore it...
}
}
// Called by Shell command.
void listSessions(int userId, IResultReceiver receiver) {
Slog.i(TAG, "listSessions() for userId " + userId);
enforceCallingPermissionForManagement();
final Bundle resultData = new Bundle();
final ArrayList<String> sessions = new ArrayList<>();
synchronized (mLock) {
if (userId != UserHandle.USER_ALL) {
final IntelligencePerUserService service = peekServiceForUserLocked(userId);
if (service != null) {
service.listSessionsLocked(sessions);
}
} else {
visitServicesLocked((s) -> s.listSessionsLocked(sessions));
}
}
resultData.putStringArrayList(RECEIVER_BUNDLE_EXTRA_SESSIONS, sessions);
try {
receiver.send(0, resultData);
} catch (RemoteException e) {
// Just ignore it...
}
}
private ActivityManagerInternal getAmInternal() {
synchronized (mLock) {
if (mAm == null) {
@@ -119,7 +183,7 @@ public final class IntelligenceManagerService extends
synchronized (mLock) {
final IntelligencePerUserService service = getServiceForUserLocked(userId);
service.startSessionLocked(activityToken, componentName, taskId, displayId,
sessionId, flags, result);
sessionId, flags, mAllowInstantService, result);
}
}
@@ -154,6 +218,14 @@ public final class IntelligenceManagerService extends
dumpLocked("", pw);
}
}
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback callback, ResultReceiver resultReceiver)
throws RemoteException {
new IntelligenceServiceShellCommand(IntelligenceManagerService.this).exec(
this, in, out, err, args, callback, resultReceiver);
}
}
private final class LocalService extends IntelligenceManagerInternal {

View File

@@ -49,6 +49,7 @@ import com.android.server.AbstractPerUserSystemService;
import com.android.server.intelligence.IntelligenceManagerInternal.AugmentedAutofillCallback;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
/**
@@ -108,7 +109,7 @@ final class IntelligencePerUserService
@GuardedBy("mLock")
public void startSessionLocked(@NonNull IBinder activityToken,
@NonNull ComponentName componentName, int taskId, int displayId,
@NonNull InteractionSessionId sessionId, int flags,
@NonNull InteractionSessionId sessionId, int flags, boolean bindInstantServiceAllowed,
@NonNull IResultReceiver resultReceiver) {
if (!isEnabledLocked()) {
sendToClient(resultReceiver, ContentCaptureManager.STATE_DISABLED);
@@ -138,9 +139,6 @@ final class IntelligencePerUserService
return;
}
// TODO(b/117779333): get from mMaster once it's moved to superclass
final boolean bindInstantServiceAllowed = false;
session = new ContentCaptureSession(getContext(), mUserId, mLock, activityToken,
this, serviceComponentName, componentName, taskId, displayId, sessionId, flags,
bindInstantServiceAllowed, mMaster.verbose);
@@ -253,6 +251,11 @@ final class IntelligencePerUserService
@GuardedBy("mLock")
public void destroyLocked() {
if (mMaster.debug) Slog.d(TAG, "destroyLocked()");
destroySessionsLocked();
}
@GuardedBy("mLock")
void destroySessionsLocked() {
final int numSessions = mSessions.size();
for (int i = 0; i < numSessions; i++) {
final ContentCaptureSession session = mSessions.valueAt(i);
@@ -261,6 +264,15 @@ final class IntelligencePerUserService
mSessions.clear();
}
@GuardedBy("mLock")
void listSessionsLocked(ArrayList<String> output) {
final int numSessions = mSessions.size();
for (int i = 0; i < numSessions; i++) {
final ContentCaptureSession session = mSessions.valueAt(i);
output.add(session.toShortString());
}
}
public AugmentedAutofillCallback requestAutofill(@NonNull IAutoFillManagerClient client,
@NonNull IBinder activityToken, int autofillSessionId, @NonNull AutofillId focusedId) {
synchronized (mLock) {

View File

@@ -0,0 +1,207 @@
/*
* Copyright (C) 2018 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.intelligence;
import static com.android.server.intelligence.IntelligenceManagerService.RECEIVER_BUNDLE_EXTRA_SESSIONS;
import android.annotation.NonNull;
import android.os.Bundle;
import android.os.ShellCommand;
import android.os.UserHandle;
import com.android.internal.os.IResultReceiver;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Shell Command implementation for {@link IntelligenceManagerService}.
*/
//TODO(b/111276913): rename once the final name is defined
public final class IntelligenceServiceShellCommand extends ShellCommand {
private final IntelligenceManagerService mService;
public IntelligenceServiceShellCommand(@NonNull IntelligenceManagerService service) {
mService = service;
}
@Override
public int onCommand(String cmd) {
if (cmd == null) {
return handleDefaultCommands(cmd);
}
final PrintWriter pw = getOutPrintWriter();
switch (cmd) {
case "list":
return requestList(pw);
case "destroy":
return requestDestroy(pw);
case "get":
return requestGet(pw);
case "set":
return requestSet(pw);
default:
return handleDefaultCommands(cmd);
}
}
@Override
public void onHelp() {
try (PrintWriter pw = getOutPrintWriter();) {
// TODO(b/111276913): rename "intelligence" once SELinux rule changed
pw.println("Intelligence Service (intelligence) commands:");
pw.println(" help");
pw.println(" Prints this help text.");
pw.println("");
pw.println(" get bind-instant-service-allowed");
pw.println(" Gets whether binding to services provided by instant apps is allowed");
pw.println("");
pw.println(" set bind-instant-service-allowed [true | false]");
pw.println(" Sets whether binding to services provided by instant apps is allowed");
pw.println("");
pw.println(" list sessions [--user USER_ID]");
pw.println(" Lists all pending sessions.");
pw.println("");
pw.println(" destroy sessions [--user USER_ID]");
pw.println(" Destroys all pending sessions.");
pw.println("");
}
}
private int requestGet(PrintWriter pw) {
final String what = getNextArgRequired();
switch(what) {
case "bind-instant-service-allowed":
return getBindInstantService(pw);
default:
pw.println("Invalid set: " + what);
return -1;
}
}
private int requestSet(PrintWriter pw) {
final String what = getNextArgRequired();
switch(what) {
case "bind-instant-service-allowed":
return setBindInstantService(pw);
default:
pw.println("Invalid set: " + what);
return -1;
}
}
private int getBindInstantService(PrintWriter pw) {
if (mService.getAllowInstantService()) {
pw.println("true");
} else {
pw.println("false");
}
return 0;
}
private int setBindInstantService(PrintWriter pw) {
final String mode = getNextArgRequired();
switch (mode.toLowerCase()) {
case "true":
mService.setAllowInstantService(true);
return 0;
case "false":
mService.setAllowInstantService(false);
return 0;
default:
pw.println("Invalid mode: " + mode);
return -1;
}
}
private int requestDestroy(PrintWriter pw) {
if (!isNextArgSessions(pw)) {
return -1;
}
final int userId = getUserIdFromArgsOrAllUsers();
final CountDownLatch latch = new CountDownLatch(1);
final IResultReceiver receiver = new IResultReceiver.Stub() {
@Override
public void send(int resultCode, Bundle resultData) {
latch.countDown();
}
};
return requestSessionCommon(pw, latch, () -> mService.destroySessions(userId, receiver));
}
private int requestList(PrintWriter pw) {
if (!isNextArgSessions(pw)) {
return -1;
}
final int userId = getUserIdFromArgsOrAllUsers();
final CountDownLatch latch = new CountDownLatch(1);
final IResultReceiver receiver = new IResultReceiver.Stub() {
@Override
public void send(int resultCode, Bundle resultData) {
final ArrayList<String> sessions = resultData
.getStringArrayList(RECEIVER_BUNDLE_EXTRA_SESSIONS);
for (String session : sessions) {
pw.println(session);
}
latch.countDown();
}
};
return requestSessionCommon(pw, latch, () -> mService.listSessions(userId, receiver));
}
private boolean isNextArgSessions(PrintWriter pw) {
final String type = getNextArgRequired();
if (!type.equals("sessions")) {
pw.println("Error: invalid list type");
return false;
}
return true;
}
private int requestSessionCommon(PrintWriter pw, CountDownLatch latch,
Runnable command) {
command.run();
return waitForLatch(pw, latch);
}
private int waitForLatch(PrintWriter pw, CountDownLatch latch) {
try {
final boolean received = latch.await(5, TimeUnit.SECONDS);
if (!received) {
pw.println("Timed out after 5 seconds");
return -1;
}
} catch (InterruptedException e) {
pw.println("System call interrupted");
Thread.currentThread().interrupt();
return -1;
}
return 0;
}
private int getUserIdFromArgsOrAllUsers() {
if ("--user".equals(getNextArg())) {
return UserHandle.parseUserArg(getNextArgRequired());
}
return UserHandle.USER_ALL;
}
}