All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.com.android.server.servicewatcher.CurrentUserServiceSupplier Maven / Gradle / Ivy

Go to download

A library jar that provides APIs for Applications written for the Google Android Platform.

There is a newer version: 15-robolectric-12650502
Show newest version
/*
 * 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.servicewatcher;

import static android.content.pm.PackageManager.GET_META_DATA;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AUTO;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.UserHandle.USER_SYSTEM;

import android.annotation.BoolRes;
import android.annotation.Nullable;
import android.annotation.StringRes;
import android.app.ActivityManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.UserHandle;
import android.permission.PermissionManager;
import android.util.Log;

import com.android.internal.util.Preconditions;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.servicewatcher.ServiceWatcher.ServiceChangedListener;
import com.android.server.servicewatcher.ServiceWatcher.ServiceSupplier;

import java.util.Comparator;
import java.util.List;
import java.util.Objects;

/**
 * Supplies services based on the current active user and version as defined in the service
 * manifest. This implementation uses {@link android.content.pm.PackageManager#MATCH_SYSTEM_ONLY} to
 * ensure only system (ie, privileged) services are matched. It also handles services that are not
 * direct boot aware, and will automatically pick the best service as the user's direct boot state
 * changes.
 *
 * 

Optionally, two permissions may be specified: (1) a caller permission - any service that does * not require callers to hold this permission is rejected (2) a service permission - any service * whose package does not hold this permission is rejected. */ public class CurrentUserServiceSupplier extends BroadcastReceiver implements ServiceSupplier { private static final String TAG = "CurrentUserServiceSupplier"; private static final String EXTRA_SERVICE_VERSION = "serviceVersion"; private static final String EXTRA_SERVICE_IS_MULTIUSER = "serviceIsMultiuser"; private static final Comparator sBoundServiceInfoComparator = (o1, o2) -> { if (o1 == o2) { return 0; } else if (o1 == null) { return -1; } else if (o2 == null) { return 1; } // ServiceInfos with higher version numbers always win. if version numbers are equal // then we prefer components that work for all users vs components that only work for a // single user at a time. otherwise everything's equal. int ret = Integer.compare(o1.getVersion(), o2.getVersion()); if (ret == 0) { if (o1.getUserId() != USER_SYSTEM && o2.getUserId() == USER_SYSTEM) { ret = -1; } else if (o1.getUserId() == USER_SYSTEM && o2.getUserId() != USER_SYSTEM) { ret = 1; } } return ret; }; /** Bound service information with version information. */ public static class BoundServiceInfo extends ServiceWatcher.BoundServiceInfo { private static int parseUid(ResolveInfo resolveInfo) { int uid = resolveInfo.serviceInfo.applicationInfo.uid; Bundle metadata = resolveInfo.serviceInfo.metaData; if (metadata != null && metadata.getBoolean(EXTRA_SERVICE_IS_MULTIUSER, false)) { // reconstruct a uid for the same app but with the system user - hope this exists uid = UserHandle.getUid(USER_SYSTEM, UserHandle.getAppId(uid)); } return uid; } private static int parseVersion(ResolveInfo resolveInfo) { int version = Integer.MIN_VALUE; if (resolveInfo.serviceInfo.metaData != null) { version = resolveInfo.serviceInfo.metaData.getInt(EXTRA_SERVICE_VERSION, version); } return version; } private final int mVersion; private final @Nullable Bundle mMetadata; protected BoundServiceInfo(String action, ResolveInfo resolveInfo) { this(action, parseUid(resolveInfo), resolveInfo.serviceInfo.getComponentName(), parseVersion(resolveInfo), resolveInfo.serviceInfo.metaData); } protected BoundServiceInfo(String action, int uid, ComponentName componentName, int version, @Nullable Bundle metadata) { super(action, uid, componentName); mVersion = version; mMetadata = metadata; } public int getVersion() { return mVersion; } public @Nullable Bundle getMetadata() { return mMetadata; } @Override public String toString() { return super.toString() + "@" + mVersion; } } private static @Nullable String retrieveExplicitPackage(Context context, @BoolRes int enableOverlayResId, @StringRes int nonOverlayPackageResId) { Resources resources = context.getResources(); boolean enableOverlay = resources.getBoolean(enableOverlayResId); if (!enableOverlay) { return resources.getString(nonOverlayPackageResId); } else { return null; } } private final Context mContext; private final ActivityManagerInternal mActivityManager; private final Intent mIntent; // a permission that the service forces callers (ie ServiceWatcher/system server) to hold private final @Nullable String mCallerPermission; // a permission that the service package should hold private final @Nullable String mServicePermission; private volatile ServiceChangedListener mListener; public CurrentUserServiceSupplier(Context context, String action) { this(context, action, null, null, null); } public CurrentUserServiceSupplier(Context context, String action, @BoolRes int enableOverlayResId, @StringRes int nonOverlayPackageResId) { this(context, action, retrieveExplicitPackage(context, enableOverlayResId, nonOverlayPackageResId), null, null); } public CurrentUserServiceSupplier(Context context, String action, @BoolRes int enableOverlayResId, @StringRes int nonOverlayPackageResId, @Nullable String callerPermission, @Nullable String servicePermission) { this(context, action, retrieveExplicitPackage(context, enableOverlayResId, nonOverlayPackageResId), callerPermission, servicePermission); } public CurrentUserServiceSupplier(Context context, String action, @Nullable String explicitPackage, @Nullable String callerPermission, @Nullable String servicePermission) { mContext = context; mActivityManager = Objects.requireNonNull( LocalServices.getService(ActivityManagerInternal.class)); mIntent = new Intent(action); if (explicitPackage != null) { mIntent.setPackage(explicitPackage); } mCallerPermission = callerPermission; mServicePermission = servicePermission; } @Override public boolean hasMatchingService() { List resolveInfos = mContext.getPackageManager() .queryIntentServicesAsUser(mIntent, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM); return !resolveInfos.isEmpty(); } @Override public void register(ServiceChangedListener listener) { Preconditions.checkState(mListener == null); mListener = listener; IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_USER_SWITCHED); intentFilter.addAction(Intent.ACTION_USER_UNLOCKED); mContext.registerReceiverAsUser(this, UserHandle.ALL, intentFilter, null, FgThread.getHandler()); } @Override public void unregister() { Preconditions.checkArgument(mListener != null); mListener = null; mContext.unregisterReceiver(this); } @Override public BoundServiceInfo getServiceInfo() { BoundServiceInfo bestServiceInfo = null; // only allow privileged services in the correct direct boot state to match int currentUserId = mActivityManager.getCurrentUserId(); List resolveInfos = mContext.getPackageManager().queryIntentServicesAsUser( mIntent, GET_META_DATA | MATCH_DIRECT_BOOT_AUTO | MATCH_SYSTEM_ONLY, currentUserId); for (ResolveInfo resolveInfo : resolveInfos) { ServiceInfo service = Objects.requireNonNull(resolveInfo.serviceInfo); if (mCallerPermission != null) { if (!mCallerPermission.equals(service.permission)) { Log.d(TAG, service.getComponentName().flattenToShortString() + " disqualified due to not requiring " + mCallerPermission); continue; } } BoundServiceInfo serviceInfo = new BoundServiceInfo(mIntent.getAction(), resolveInfo); if (mServicePermission != null) { if (PermissionManager.checkPackageNamePermission(mServicePermission, service.packageName, serviceInfo.getUserId()) != PERMISSION_GRANTED) { Log.d(TAG, serviceInfo.getComponentName().flattenToShortString() + " disqualified due to not holding " + mCallerPermission); continue; } } if (sBoundServiceInfoComparator.compare(serviceInfo, bestServiceInfo) > 0) { bestServiceInfo = serviceInfo; } } return bestServiceInfo; } @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action == null) { return; } int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); if (userId == UserHandle.USER_NULL) { return; } ServiceChangedListener listener = mListener; if (listener == null) { return; } switch (action) { case Intent.ACTION_USER_SWITCHED: listener.onServiceChanged(); break; case Intent.ACTION_USER_UNLOCKED: // user unlocked implies direct boot mode may have changed if (userId == mActivityManager.getCurrentUserId()) { listener.onServiceChanged(); } break; default: break; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy