blob: a075c50733d583ef965110efbdc80f576b05c07e [file] [log] [blame]
/*
* 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();
}
}
}
}