/*
|
* 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.appbinding.finders;
|
|
import android.annotation.NonNull;
|
import android.annotation.Nullable;
|
import android.content.Context;
|
import android.content.pm.IPackageManager;
|
import android.content.pm.ServiceInfo;
|
import android.os.Handler;
|
import android.os.IBinder;
|
import android.os.IInterface;
|
import android.util.Log;
|
import android.util.Slog;
|
import android.util.SparseArray;
|
|
import com.android.internal.annotations.GuardedBy;
|
import com.android.server.appbinding.AppBindingConstants;
|
import com.android.server.appbinding.AppBindingService;
|
import com.android.server.appbinding.AppBindingUtils;
|
|
import java.io.PrintWriter;
|
import java.util.function.BiConsumer;
|
|
/**
|
* Baseclss that finds "persistent" service from a type of an app.
|
*
|
* @param <TServiceType> Type of the target service class.
|
* @param <TServiceInterfaceType> Type of the IInterface class used by TServiceType.
|
*/
|
public abstract class AppServiceFinder<TServiceType, TServiceInterfaceType extends IInterface> {
|
protected static final String TAG = AppBindingService.TAG;
|
protected static final boolean DEBUG = AppBindingService.DEBUG;
|
|
protected final Context mContext;
|
protected final BiConsumer<AppServiceFinder, Integer> mListener;
|
protected final Handler mHandler;
|
|
private final Object mLock = new Object();
|
|
@GuardedBy("mLock")
|
private final SparseArray<String> mTargetPackages = new SparseArray(4);
|
|
@GuardedBy("mLock")
|
private final SparseArray<ServiceInfo> mTargetServices = new SparseArray(4);
|
|
@GuardedBy("mLock")
|
private final SparseArray<String> mLastMessages = new SparseArray(4);
|
|
public AppServiceFinder(Context context,
|
BiConsumer<AppServiceFinder, Integer> listener,
|
Handler callbackHandler) {
|
mContext = context;
|
mListener = listener;
|
mHandler = callbackHandler;
|
}
|
|
/** Whether this service should really be enabled. */
|
protected boolean isEnabled(AppBindingConstants constants) {
|
return true;
|
}
|
|
/** Human readable description of the type of apps; e.g. [Default SMS app] */
|
@NonNull
|
public abstract String getAppDescription();
|
|
/** Start monitoring apps. (e.g. Start watching the default SMS app changes.) */
|
public void startMonitoring() {
|
}
|
|
/** Called when a user is removed. */
|
public void onUserRemoved(int userId) {
|
synchronized (mLock) {
|
mTargetPackages.delete(userId);
|
mTargetServices.delete(userId);
|
mLastMessages.delete(userId);
|
}
|
}
|
|
/**
|
* Find the target service from the target app on a given user.
|
*/
|
@Nullable
|
public final ServiceInfo findService(int userId, IPackageManager ipm,
|
AppBindingConstants constants) {
|
synchronized (mLock) {
|
mTargetPackages.put(userId, null);
|
mTargetServices.put(userId, null);
|
mLastMessages.put(userId, null);
|
|
if (!isEnabled(constants)) {
|
final String message = "feature disabled";
|
mLastMessages.put(userId, message);
|
Slog.i(TAG, getAppDescription() + " " + message);
|
return null;
|
}
|
|
final String targetPackage = getTargetPackage(userId);
|
if (DEBUG) {
|
Slog.d(TAG, getAppDescription() + " package=" + targetPackage);
|
}
|
if (targetPackage == null) {
|
final String message = "Target package not found";
|
mLastMessages.put(userId, message);
|
Slog.w(TAG, getAppDescription() + " u" + userId + " " + message);
|
return null;
|
}
|
mTargetPackages.put(userId, targetPackage);
|
|
final StringBuilder errorMessage = new StringBuilder();
|
final ServiceInfo service = AppBindingUtils.findService(
|
targetPackage,
|
userId,
|
getServiceAction(),
|
getServicePermission(),
|
getServiceClass(),
|
ipm,
|
errorMessage);
|
|
if (service == null) {
|
final String message = errorMessage.toString();
|
mLastMessages.put(userId, message);
|
if (DEBUG) {
|
// This log is optional because findService() already did Log.e().
|
Slog.w(TAG, getAppDescription() + " package " + targetPackage + " u" + userId
|
+ " " + message);
|
}
|
return null;
|
}
|
final String error = validateService(service);
|
if (error != null) {
|
mLastMessages.put(userId, error);
|
Log.e(TAG, error);
|
return null;
|
}
|
|
final String message = "Valid service found";
|
mLastMessages.put(userId, message);
|
mTargetServices.put(userId, service);
|
return service;
|
}
|
}
|
|
protected abstract Class<TServiceType> getServiceClass();
|
|
/**
|
* Convert a binder reference to a service interface type.
|
*/
|
public abstract TServiceInterfaceType asInterface(IBinder obj);
|
|
/**
|
* @return the target package on a given user.
|
*/
|
@Nullable
|
public abstract String getTargetPackage(int userId);
|
|
/**
|
* @return the intent action that identifies the target service in the target app.
|
*/
|
@NonNull
|
protected abstract String getServiceAction();
|
|
/**
|
* @return the permission that the target service must be protected with.
|
*/
|
@NonNull
|
protected abstract String getServicePermission();
|
|
/**
|
* Subclass can implement it to decide whether to accept a service (by returning null) or not
|
* (by returning an error message.)
|
*/
|
protected String validateService(ServiceInfo service) {
|
return null;
|
}
|
|
/** Return the bind flags for this service. */
|
public abstract int getBindFlags(AppBindingConstants constants);
|
|
/** Dumpsys support. */
|
public void dump(String prefix, PrintWriter pw) {
|
pw.print(prefix);
|
pw.print("App type: ");
|
pw.print(getAppDescription());
|
pw.println();
|
|
synchronized (mLock) {
|
for (int i = 0; i < mTargetPackages.size(); i++) {
|
final int userId = mTargetPackages.keyAt(i);
|
pw.print(prefix);
|
pw.print(" User: ");
|
pw.print(userId);
|
pw.println();
|
|
pw.print(prefix);
|
pw.print(" Package: ");
|
pw.print(mTargetPackages.get(userId));
|
pw.println();
|
|
pw.print(prefix);
|
pw.print(" Service: ");
|
pw.print(mTargetServices.get(userId));
|
pw.println();
|
|
pw.print(prefix);
|
pw.print(" Message: ");
|
pw.print(mLastMessages.get(userId));
|
pw.println();
|
}
|
}
|
}
|
|
/** Dumpys support */
|
public void dumpSimple(PrintWriter pw) {
|
synchronized (mLock) {
|
for (int i = 0; i < mTargetPackages.size(); i++) {
|
final int userId = mTargetPackages.keyAt(i);
|
pw.print("finder,");
|
pw.print(getAppDescription());
|
pw.print(",");
|
pw.print(userId);
|
pw.print(",");
|
pw.print(mTargetPackages.get(userId));
|
pw.print(",");
|
pw.print(mTargetServices.get(userId));
|
pw.print(",");
|
pw.print(mLastMessages.get(userId));
|
pw.println();
|
}
|
}
|
}
|
}
|