blob: 7e812dc829e6a0e492664b5c235814cef42db7a2 [file] [log] [blame]
/*
* Copyright (C) 2022 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.adservices.service.devapi;
import android.annotation.NonNull;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.provider.Settings;
import com.android.adservices.LogUtil;
import com.android.adservices.service.common.SdkRuntimeUtil;
import com.android.adservices.service.common.compat.BuildCompatUtils;
import com.android.adservices.service.common.compat.PackageManagerCompatUtils;
import com.android.internal.annotations.VisibleForTesting;
import java.util.Objects;
/**
* Creates a {@link DevContext} instance using the information related to the caller of the current
* API.
*/
public class DevContextFilter {
private final ContentResolver mContentResolver;
private final AppPackageNameRetriever mAppPackageNameRetriever;
private final PackageManager mPackageManager;
@VisibleForTesting
DevContextFilter(
@NonNull ContentResolver contentResolver,
@NonNull PackageManager packageManager,
@NonNull AppPackageNameRetriever appPackageNameRetriever) {
Objects.requireNonNull(contentResolver);
Objects.requireNonNull(packageManager);
Objects.requireNonNull(appPackageNameRetriever);
mAppPackageNameRetriever = appPackageNameRetriever;
mContentResolver = contentResolver;
mPackageManager = packageManager;
}
/** Creates an instance of {@link DevContextFilter}. */
public static DevContextFilter create(@NonNull Context context) {
Objects.requireNonNull(context);
return new DevContextFilter(
context.getContentResolver(),
context.getPackageManager(),
AppPackageNameRetriever.create(context));
}
/**
* Creates a {@link DevContext} for the current binder call. It is assumed to be called by APIs
* after having collected the caller UID in the API thread..
*
* @return A dev context specifying if the developer options are enabled for this API call or a
* context with developer options disabled if there is any error retrieving info for the
* calling application.
* @throws IllegalStateException if the current thread is not currently executing an incoming
* transaction.
*/
public DevContext createDevContext() throws IllegalStateException {
return createDevContextFromCallingUid(Binder.getCallingUidOrThrow());
}
/**
* Creates a {@link DevContext} for a given Binder calling UID.
*
* @param callingUid The Binder calling UID.
* @return A dev context specifying if the developer options are enabled for this API call or a
* context with developer options disabled if there is any error retrieving info for the
* calling application.
*/
public DevContext createDevContextFromCallingUid(int callingUid) {
return createDevContext(SdkRuntimeUtil.getCallingAppUid(callingUid));
}
/**
* Creates a {@link DevContext} for a given app UID.
*
* @param callingAppUid The UID of the caller APP.
* @return A dev context specifying if the developer options are enabled for this API call or a
* context with developer options disabled if there is any error retrieving info for the
* calling application.
*/
@VisibleForTesting
public DevContext createDevContext(int callingAppUid) {
if (!isDeveloperMode()) {
return DevContext.createForDevOptionsDisabled();
}
try {
String callingAppPackage =
mAppPackageNameRetriever.getAppPackageNameForUid(callingAppUid);
LogUtil.v("Creating Dev Context for calling app with package " + callingAppPackage);
if (!isDebuggable(callingAppPackage)) {
LogUtil.v("Non debuggable, ignoring");
return DevContext.createForDevOptionsDisabled();
}
return DevContext.builder()
.setDevOptionsEnabled(true)
.setCallingAppPackageName(callingAppPackage)
.build();
} catch (IllegalArgumentException e) {
LogUtil.w(
"Unable to retrieve the package name for UID %d. Creating a DevContext with "
+ "developer options disabled.",
callingAppUid);
return DevContext.createForDevOptionsDisabled();
}
}
/**
* Returns true if the callingAppPackage is debuggable and false if it is not or if {@code
* callingAppPackage} is null.
*
* @param callingAppPackage the calling app package
*/
@VisibleForTesting
public boolean isDebuggable(String callingAppPackage) {
if (Objects.isNull(callingAppPackage)) {
return false;
}
try {
ApplicationInfo applicationInfo =
PackageManagerCompatUtils.getApplicationInfo(
mPackageManager, callingAppPackage, 0);
return (applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
} catch (PackageManager.NameNotFoundException e) {
LogUtil.w(
"Unable to retrieve application info for app with ID %d and resolved package "
+ "name '%s', considering not debuggable for safety.",
callingAppPackage, callingAppPackage);
return false;
}
}
/** Returns true if developer options are enabled. */
@VisibleForTesting
public boolean isDeveloperMode() {
return BuildCompatUtils.isDebuggable()
|| Settings.Global.getInt(
mContentResolver, Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0)
!= 0;
}
}