summaryrefslogtreecommitdiff
path: root/services/print
diff options
context:
space:
mode:
authorEugene Susla <eugenesusla@google.com>2017-04-18 10:56:37 -0700
committerEugene Susla <eugenesusla@google.com>2017-04-18 12:00:11 -0700
commit0d3dd12fa0ba38ffedbb18b55886306acf98988e (patch)
tree49b414539bd72af591896908b154e5e9ec203762 /services/print
parent5b06dbd12bcd28fbc051c88c3ea7f31bcf20d1f8 (diff)
Move CompanionDeviceManagerService to a place of its own
Fixes: 37473452 Test: Ensure companion devi9ce functionality still works Change-Id: I89624217373b3e77296c71f9429387d99d15e236
Diffstat (limited to 'services/print')
-rw-r--r--services/print/java/com/android/server/print/CompanionDeviceManagerService.java540
1 files changed, 0 insertions, 540 deletions
diff --git a/services/print/java/com/android/server/print/CompanionDeviceManagerService.java b/services/print/java/com/android/server/print/CompanionDeviceManagerService.java
deleted file mode 100644
index 122a954b4d30..000000000000
--- a/services/print/java/com/android/server/print/CompanionDeviceManagerService.java
+++ /dev/null
@@ -1,540 +0,0 @@
-/*
- * Copyright (C) 2017 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.print;
-
-import static com.android.internal.util.CollectionUtils.size;
-import static com.android.internal.util.Preconditions.checkArgument;
-import static com.android.internal.util.Preconditions.checkNotNull;
-import static com.android.internal.util.Preconditions.checkState;
-
-import android.Manifest;
-import android.annotation.CheckResult;
-import android.annotation.Nullable;
-import android.app.PendingIntent;
-import android.companion.AssociationRequest;
-import android.companion.CompanionDeviceManager;
-import android.companion.ICompanionDeviceDiscoveryService;
-import android.companion.ICompanionDeviceDiscoveryServiceCallback;
-import android.companion.ICompanionDeviceManager;
-import android.companion.IFindDeviceCallback;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.net.NetworkPolicyManager;
-import android.os.Binder;
-import android.os.Environment;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.IDeviceIdleController;
-import android.os.IInterface;
-import android.os.Parcel;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.provider.SettingsStringUtil.ComponentNameSet;
-import android.text.BidiFormatter;
-import android.util.AtomicFile;
-import android.util.ExceptionUtils;
-import android.util.Log;
-import android.util.Slog;
-import android.util.Xml;
-
-import com.android.internal.app.IAppOpsService;
-import com.android.internal.content.PackageMonitor;
-import com.android.internal.notification.NotificationAccessConfirmationActivityContract;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.CollectionUtils;
-import com.android.server.FgThread;
-import com.android.server.SystemService;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.function.Function;
-
-//TODO move to own package!
-//TODO onStop schedule unbind in 5 seconds
-//TODO make sure APIs are only callable from currently focused app
-//TODO schedule stopScan on activity destroy(except if configuration change)
-//TODO on associate called again after configuration change -> replace old callback with new
-//TODO avoid leaking calling activity in IFindDeviceCallback (see PrintManager#print for example)
-//TODO check user-feature present in manifest on API calls
-/** @hide */
-public class CompanionDeviceManagerService extends SystemService implements Binder.DeathRecipient {
-
- private static final ComponentName SERVICE_TO_BIND_TO = ComponentName.createRelative(
- CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME,
- ".DeviceDiscoveryService");
-
- private static final boolean DEBUG = false;
- private static final String LOG_TAG = "CompanionDeviceManagerService";
-
- private static final String XML_TAG_ASSOCIATIONS = "associations";
- private static final String XML_TAG_ASSOCIATION = "association";
- private static final String XML_ATTR_PACKAGE = "package";
- private static final String XML_ATTR_DEVICE = "device";
- private static final String XML_FILE_NAME = "companion_device_manager_associations.xml";
-
- private final CompanionDeviceManagerImpl mImpl;
- private final ConcurrentMap<Integer, AtomicFile> mUidToStorage = new ConcurrentHashMap<>();
- private IDeviceIdleController mIdleController;
- private IFindDeviceCallback mFindDeviceCallback;
- private ServiceConnection mServiceConnection;
- private IAppOpsService mAppOpsManager;
-
- public CompanionDeviceManagerService(Context context) {
- super(context);
- mImpl = new CompanionDeviceManagerImpl();
- mIdleController = IDeviceIdleController.Stub.asInterface(
- ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
- mAppOpsManager = IAppOpsService.Stub.asInterface(
- ServiceManager.getService(Context.APP_OPS_SERVICE));
- registerPackageMonitor();
- }
-
- private void registerPackageMonitor() {
- new PackageMonitor() {
- @Override
- public void onPackageRemoved(String packageName, int uid) {
- updateAssociations(
- as -> CollectionUtils.filter(as,
- a -> !Objects.equals(a.companionAppPackage, packageName)),
- getChangingUserId());
- }
-
- @Override
- public void onPackageModified(String packageName) {
- int userId = getChangingUserId();
- if (!ArrayUtils.isEmpty(readAllAssociations(userId, packageName))) {
- updateSpecialAccessPermissionForAssociatedPackage(packageName, userId);
- }
- }
-
- }.register(getContext(), FgThread.get().getLooper(), UserHandle.ALL, true);
- }
-
- @Override
- public void onStart() {
- publishBinderService(Context.COMPANION_DEVICE_SERVICE, mImpl);
- }
-
- @Override
- public void binderDied() {
- Handler.getMain().post(this::cleanup);
- }
-
- private void cleanup() {
- mServiceConnection = unbind(mServiceConnection);
- mFindDeviceCallback = unlinkToDeath(mFindDeviceCallback, this, 0);
- }
-
- /**
- * Usage: {@code a = unlinkToDeath(a, deathRecipient, flags); }
- */
- @Nullable
- @CheckResult
- private static <T extends IInterface> T unlinkToDeath(T iinterface,
- IBinder.DeathRecipient deathRecipient, int flags) {
- if (iinterface != null) {
- iinterface.asBinder().unlinkToDeath(deathRecipient, flags);
- }
- return null;
- }
-
- @Nullable
- @CheckResult
- private ServiceConnection unbind(@Nullable ServiceConnection conn) {
- if (conn != null) {
- getContext().unbindService(conn);
- }
- return null;
- }
-
- class CompanionDeviceManagerImpl extends ICompanionDeviceManager.Stub {
-
- @Override
- public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
- throws RemoteException {
- try {
- return super.onTransact(code, data, reply, flags);
- } catch (Throwable e) {
- Slog.e(LOG_TAG, "Error during IPC", e);
- throw ExceptionUtils.propagate(e, RemoteException.class);
- }
- }
-
- @Override
- public void associate(
- AssociationRequest request,
- IFindDeviceCallback callback,
- String callingPackage) throws RemoteException {
- if (DEBUG) {
- Slog.i(LOG_TAG, "associate(request = " + request + ", callback = " + callback
- + ", callingPackage = " + callingPackage + ")");
- }
- checkNotNull(request, "Request cannot be null");
- checkNotNull(callback, "Callback cannot be null");
- checkCallerIsSystemOr(callingPackage);
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- //TODO bindServiceAsUser
- getContext().bindService(
- new Intent().setComponent(SERVICE_TO_BIND_TO),
- createServiceConnection(request, callback, callingPackage),
- Context.BIND_AUTO_CREATE);
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- }
- }
-
- @Override
- public List<String> getAssociations(String callingPackage, int userId)
- throws RemoteException {
- checkCallerIsSystemOr(callingPackage, userId);
- return CollectionUtils.map(
- readAllAssociations(userId, callingPackage),
- a -> a.deviceAddress);
- }
-
- //TODO also revoke notification access
- @Override
- public void disassociate(String deviceMacAddress, String callingPackage)
- throws RemoteException {
- checkNotNull(deviceMacAddress);
- checkCallerIsSystemOr(callingPackage);
- updateAssociations(associations -> CollectionUtils.remove(associations,
- new Association(getCallingUserId(), deviceMacAddress, callingPackage)));
- }
-
- private void checkCallerIsSystemOr(String pkg) throws RemoteException {
- checkCallerIsSystemOr(pkg, getCallingUserId());
- }
-
- private void checkCallerIsSystemOr(String pkg, int userId) throws RemoteException {
- if (getCallingUserId() == UserHandle.USER_SYSTEM) {
- return;
- }
-
- checkArgument(getCallingUserId() == userId,
- "Must be called by either same user or system");
- mAppOpsManager.checkPackage(Binder.getCallingUid(), pkg);
- }
-
- @Override
- public PendingIntent requestNotificationAccess(ComponentName component)
- throws RemoteException {
- String callingPackage = component.getPackageName();
- checkCanCallNotificationApi(callingPackage);
- int userId = getCallingUserId();
- String packageTitle = BidiFormatter.getInstance().unicodeWrap(
- getPackageInfo(callingPackage, userId)
- .applicationInfo
- .loadSafeLabel(getContext().getPackageManager())
- .toString());
- long identity = Binder.clearCallingIdentity();
- try {
- return PendingIntent.getActivity(getContext(),
- 0 /* request code */,
- NotificationAccessConfirmationActivityContract.launcherIntent(
- userId, component, packageTitle),
- PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT
- | PendingIntent.FLAG_CANCEL_CURRENT);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- @Override
- public boolean hasNotificationAccess(ComponentName component) throws RemoteException {
- checkCanCallNotificationApi(component.getPackageName());
- String setting = Settings.Secure.getString(getContext().getContentResolver(),
- Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
- return new ComponentNameSet(setting).contains(component);
- }
-
- private void checkCanCallNotificationApi(String callingPackage) throws RemoteException {
- checkCallerIsSystemOr(callingPackage);
- checkState(!ArrayUtils.isEmpty(readAllAssociations(getCallingUserId(), callingPackage)),
- "App must have an association before calling this API");
- }
- }
-
-
- private int getCallingUserId() {
- return UserHandle.getUserId(Binder.getCallingUid());
- }
-
- private ServiceConnection createServiceConnection(
- final AssociationRequest request,
- final IFindDeviceCallback findDeviceCallback,
- final String callingPackage) {
- mServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- if (DEBUG) {
- Slog.i(LOG_TAG,
- "onServiceConnected(name = " + name + ", service = "
- + service + ")");
- }
- mFindDeviceCallback = findDeviceCallback;
- try {
- mFindDeviceCallback.asBinder().linkToDeath(
- CompanionDeviceManagerService.this, 0);
- } catch (RemoteException e) {
- cleanup();
- return;
- }
- try {
- ICompanionDeviceDiscoveryService.Stub
- .asInterface(service)
- .startDiscovery(
- request,
- callingPackage,
- findDeviceCallback,
- getServiceCallback());
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- if (DEBUG) Slog.i(LOG_TAG, "onServiceDisconnected(name = " + name + ")");
- }
- };
- return mServiceConnection;
- }
-
- private ICompanionDeviceDiscoveryServiceCallback.Stub getServiceCallback() {
- return new ICompanionDeviceDiscoveryServiceCallback.Stub() {
-
- @Override
- public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
- throws RemoteException {
- try {
- return super.onTransact(code, data, reply, flags);
- } catch (Throwable e) {
- Slog.e(LOG_TAG, "Error during IPC", e);
- throw ExceptionUtils.propagate(e, RemoteException.class);
- }
- }
-
- @Override
- public void onDeviceSelected(String packageName, int userId, String deviceAddress) {
- updateSpecialAccessPermissionForAssociatedPackage(packageName, userId);
- recordAssociation(packageName, deviceAddress);
- cleanup();
- }
-
- @Override
- public void onDeviceSelectionCancel() {
- cleanup();
- }
- };
- }
-
- private void updateSpecialAccessPermissionForAssociatedPackage(String packageName, int userId) {
- PackageInfo packageInfo = getPackageInfo(packageName, userId);
- if (packageInfo == null) {
- return;
- }
-
- Binder.withCleanCallingIdentity(() -> {
- try {
- if (ArrayUtils.contains(packageInfo.requestedPermissions,
- Manifest.permission.RUN_IN_BACKGROUND)) {
- mIdleController.addPowerSaveWhitelistApp(packageInfo.packageName);
- } else {
- mIdleController.removePowerSaveWhitelistApp(packageInfo.packageName);
- }
- } catch (RemoteException e) {
- /* ignore - local call */
- }
-
- NetworkPolicyManager networkPolicyManager = NetworkPolicyManager.from(getContext());
- if (ArrayUtils.contains(packageInfo.requestedPermissions,
- Manifest.permission.USE_DATA_IN_BACKGROUND)) {
- networkPolicyManager.addUidPolicy(
- packageInfo.applicationInfo.uid,
- NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND);
- } else {
- networkPolicyManager.removeUidPolicy(
- packageInfo.applicationInfo.uid,
- NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND);
- }
- });
- }
-
- @Nullable
- private PackageInfo getPackageInfo(String packageName, int userId) {
- return Binder.withCleanCallingIdentity(() -> {
- try {
- return getContext().getPackageManager().getPackageInfoAsUser(
- packageName, PackageManager.GET_PERMISSIONS, userId);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.e(LOG_TAG, "Failed to get PackageInfo for package " + packageName, e);
- return null;
- }
- });
- }
-
- private void recordAssociation(String priviledgedPackage, String deviceAddress) {
- if (DEBUG) {
- Log.i(LOG_TAG, "recordAssociation(priviledgedPackage = " + priviledgedPackage
- + ", deviceAddress = " + deviceAddress + ")");
- }
- int userId = getCallingUserId();
- updateAssociations(associations -> CollectionUtils.add(associations,
- new Association(userId, deviceAddress, priviledgedPackage)));
- }
-
- private void updateAssociations(Function<List<Association>, List<Association>> update) {
- updateAssociations(update, getCallingUserId());
- }
-
- private void updateAssociations(Function<List<Association>, List<Association>> update,
- int userId) {
- final AtomicFile file = getStorageFileForUser(userId);
- synchronized (file) {
- List<Association> associations = readAllAssociations(userId);
- final List<Association> old = CollectionUtils.copyOf(associations);
- associations = update.apply(associations);
- if (size(old) == size(associations)) return;
-
- List<Association> finalAssociations = associations;
- file.write((out) -> {
- XmlSerializer xml = Xml.newSerializer();
- try {
- xml.setOutput(out, StandardCharsets.UTF_8.name());
- xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
- xml.startDocument(null, true);
- xml.startTag(null, XML_TAG_ASSOCIATIONS);
-
- for (int i = 0; i < size(finalAssociations); i++) {
- Association association = finalAssociations.get(i);
- xml.startTag(null, XML_TAG_ASSOCIATION)
- .attribute(null, XML_ATTR_PACKAGE, association.companionAppPackage)
- .attribute(null, XML_ATTR_DEVICE, association.deviceAddress)
- .endTag(null, XML_TAG_ASSOCIATION);
- }
-
- xml.endTag(null, XML_TAG_ASSOCIATIONS);
- xml.endDocument();
- } catch (Exception e) {
- Slog.e(LOG_TAG, "Error while writing associations file", e);
- throw ExceptionUtils.propagate(e);
- }
-
- });
- }
- }
-
- private AtomicFile getStorageFileForUser(int uid) {
- return mUidToStorage.computeIfAbsent(uid, (u) ->
- new AtomicFile(new File(
- //TODO deprecated method - what's the right replacement?
- Environment.getUserSystemDirectory(u),
- XML_FILE_NAME)));
- }
-
- @Nullable
- private ArrayList<Association> readAllAssociations(int uid) {
- return readAllAssociations(uid, null);
- }
-
- @Nullable
- private ArrayList<Association> readAllAssociations(int userId, @Nullable String packageFilter) {
- final AtomicFile file = getStorageFileForUser(userId);
-
- if (!file.getBaseFile().exists()) return null;
-
- ArrayList<Association> result = null;
- final XmlPullParser parser = Xml.newPullParser();
- synchronized (file) {
- try (FileInputStream in = file.openRead()) {
- parser.setInput(in, StandardCharsets.UTF_8.name());
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
- if (type != XmlPullParser.START_TAG
- && !XML_TAG_ASSOCIATIONS.equals(parser.getName())) continue;
-
- final String appPackage = parser.getAttributeValue(null, XML_ATTR_PACKAGE);
- final String deviceAddress = parser.getAttributeValue(null, XML_ATTR_DEVICE);
-
- if (appPackage == null || deviceAddress == null) continue;
- if (packageFilter != null && !packageFilter.equals(appPackage)) continue;
-
- result = ArrayUtils.add(result,
- new Association(userId, deviceAddress, appPackage));
- }
- return result;
- } catch (XmlPullParserException | IOException e) {
- Slog.e(LOG_TAG, "Error while reading associations file", e);
- return null;
- }
- }
- }
-
- private class Association {
- public final int uid;
- public final String deviceAddress;
- public final String companionAppPackage;
-
- private Association(int uid, String deviceAddress, String companionAppPackage) {
- this.uid = uid;
- this.deviceAddress = checkNotNull(deviceAddress);
- this.companionAppPackage = checkNotNull(companionAppPackage);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- Association that = (Association) o;
-
- if (uid != that.uid) return false;
- if (!deviceAddress.equals(that.deviceAddress)) return false;
- return companionAppPackage.equals(that.companionAppPackage);
-
- }
-
- @Override
- public int hashCode() {
- int result = uid;
- result = 31 * result + deviceAddress.hashCode();
- result = 31 * result + companionAppPackage.hashCode();
- return result;
- }
- }
-
-}