11002 lines
442 KiB
Java
11002 lines
442 KiB
Java
/*
|
|
* Copyright (C) 2012 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 android.app;
|
|
|
|
|
|
import static android.location.flags.Flags.FLAG_LOCATION_BYPASS;
|
|
import static android.media.audio.Flags.roForegroundAudioControl;
|
|
import static android.permission.flags.Flags.FLAG_OP_ENABLE_MOBILE_DATA_BY_USER;
|
|
import static android.view.contentprotection.flags.Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED;
|
|
import static android.view.contentprotection.flags.Flags.FLAG_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER_APP_OP_ENABLED;
|
|
|
|
import static java.lang.Long.max;
|
|
|
|
import android.Manifest;
|
|
import android.annotation.CallbackExecutor;
|
|
import android.annotation.FlaggedApi;
|
|
import android.annotation.IntDef;
|
|
import android.annotation.IntRange;
|
|
import android.annotation.NonNull;
|
|
import android.annotation.Nullable;
|
|
import android.annotation.RequiresPermission;
|
|
import android.annotation.StringDef;
|
|
import android.annotation.SuppressLint;
|
|
import android.annotation.SystemApi;
|
|
import android.annotation.SystemService;
|
|
import android.annotation.TestApi;
|
|
import android.app.usage.UsageStatsManager;
|
|
import android.companion.virtual.VirtualDeviceManager;
|
|
import android.compat.Compatibility;
|
|
import android.compat.annotation.ChangeId;
|
|
import android.compat.annotation.EnabledAfter;
|
|
import android.compat.annotation.UnsupportedAppUsage;
|
|
import android.content.AttributionSource;
|
|
import android.content.ComponentName;
|
|
import android.content.ContentResolver;
|
|
import android.content.Context;
|
|
import android.content.pm.ApplicationInfo;
|
|
import android.content.pm.PackageManager;
|
|
import android.content.pm.ParceledListSlice;
|
|
import android.database.DatabaseUtils;
|
|
import android.health.connect.HealthConnectManager;
|
|
import android.media.AudioAttributes.AttributeUsage;
|
|
import android.media.MediaRouter2;
|
|
import android.os.Binder;
|
|
import android.os.Build;
|
|
import android.os.Handler;
|
|
import android.os.HandlerExecutor;
|
|
import android.os.HandlerThread;
|
|
import android.os.IBinder;
|
|
import android.os.Looper;
|
|
import android.os.PackageTagsList;
|
|
import android.os.Parcel;
|
|
import android.os.Parcelable;
|
|
import android.os.Process;
|
|
import android.os.RemoteCallback;
|
|
import android.os.RemoteException;
|
|
import android.os.ServiceManager;
|
|
import android.os.SystemClock;
|
|
import android.os.UserHandle;
|
|
import android.os.UserManager;
|
|
import android.permission.PermissionGroupUsage;
|
|
import android.permission.PermissionUsageHelper;
|
|
import android.permission.flags.Flags;
|
|
import android.provider.DeviceConfig;
|
|
import android.util.ArrayMap;
|
|
import android.util.ArraySet;
|
|
import android.util.Log;
|
|
import android.util.LongSparseArray;
|
|
import android.util.LongSparseLongArray;
|
|
import android.util.Pools;
|
|
import android.util.SparseArray;
|
|
|
|
import com.android.internal.annotations.GuardedBy;
|
|
import com.android.internal.annotations.Immutable;
|
|
import com.android.internal.app.IAppOpsActiveCallback;
|
|
import com.android.internal.app.IAppOpsAsyncNotedCallback;
|
|
import com.android.internal.app.IAppOpsCallback;
|
|
import com.android.internal.app.IAppOpsNotedCallback;
|
|
import com.android.internal.app.IAppOpsService;
|
|
import com.android.internal.app.IAppOpsStartedCallback;
|
|
import com.android.internal.app.MessageSamplingConfig;
|
|
import com.android.internal.os.RuntimeInit;
|
|
import com.android.internal.os.ZygoteInit;
|
|
import com.android.internal.util.ArrayUtils;
|
|
import com.android.internal.util.DataClass;
|
|
import com.android.internal.util.FrameworkStatsLog;
|
|
import com.android.internal.util.Parcelling;
|
|
import com.android.internal.util.Preconditions;
|
|
|
|
import java.lang.annotation.ElementType;
|
|
import java.lang.annotation.Retention;
|
|
import java.lang.annotation.RetentionPolicy;
|
|
import java.lang.annotation.Target;
|
|
import java.lang.reflect.Method;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.BitSet;
|
|
import java.util.Collection;
|
|
import java.util.Collections;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Objects;
|
|
import java.util.concurrent.Executor;
|
|
import java.util.function.Consumer;
|
|
import java.util.function.Supplier;
|
|
|
|
/**
|
|
* App-ops are used for two purposes: Access control and tracking.
|
|
*
|
|
* <p>App-ops cover a wide variety of functionality from helping with runtime permissions access
|
|
* control and tracking to battery consumption tracking.
|
|
*
|
|
* <h2>Access control</h2>
|
|
*
|
|
* <p>App-ops can either be controlled for each uid or for each package. Which one is used depends
|
|
* on the API provider maintaining this app-op. For any security or privacy related app-op the
|
|
* provider needs to control the app-op for per uid as all security and privacy is based on uid in
|
|
* Android.
|
|
*
|
|
* <p>To control access the app-op can be set to a mode to:
|
|
* <dl>
|
|
* <dt>{@link #MODE_DEFAULT}
|
|
* <dd>Default behavior, might differ from app-op or app-op
|
|
* <dt>{@link #MODE_ALLOWED}
|
|
* <dd>Allow the access
|
|
* <dt>{@link #MODE_IGNORED}
|
|
* <dd>Don't allow the access, i.e. don't perform the requested action or return no or
|
|
* placeholder data
|
|
* <dt>{@link #MODE_ERRORED}
|
|
* <dd>Throw a {@link SecurityException} on access. This can be suppressed by using a
|
|
* {@code ...noThrow} method to check the mode
|
|
* </dl>
|
|
*
|
|
* <p>API providers need to check the mode returned by {@link #noteOp} if they are are allowing
|
|
* access to operations gated by the app-op. {@link #unsafeCheckOp} should be used to check the
|
|
* mode if no access is granted. E.g. this can be used for displaying app-op state in the UI or
|
|
* when checking the state before later calling {@link #noteOp} anyway.
|
|
*
|
|
* <p>If an operation refers to a time span (e.g. a audio-recording session) the API provider
|
|
* should use {@link #startOp} and {@link #finishOp} instead of {@link #noteOp}.
|
|
*
|
|
* <h3>Runtime permissions and app-ops</h3>
|
|
*
|
|
* <p>Each platform defined runtime permission (beside background modifiers) has an associated app
|
|
* op which is used for tracking but also to allow for silent failures. I.e. if the runtime
|
|
* permission is denied the caller gets a {@link SecurityException}, but if the permission is
|
|
* granted and the app-op is {@link #MODE_IGNORED} then the callers gets placeholder behavior, e.g.
|
|
* location callbacks would not happen.
|
|
*
|
|
* <h3>App-op permissions</h3>
|
|
*
|
|
* <p>App-ops permissions are platform defined permissions that can be overridden. The security
|
|
* check for app-op permissions should by {@link #MODE_DEFAULT default} check the permission grant
|
|
* state. If the app-op state is set to {@link #MODE_ALLOWED} or {@link #MODE_IGNORED} the app-op
|
|
* state should be checked instead of the permission grant state.
|
|
*
|
|
* <p>This functionality allows to grant access by default to apps fulfilling the requirements for
|
|
* a certain permission level. Still the behavior can be overridden when needed.
|
|
*
|
|
* <h2>Tracking</h2>
|
|
*
|
|
* <p>App-ops track many important events, including all accesses to runtime permission protected
|
|
* APIs. This is done by tracking when an app-op was {@link #noteOp noted} or
|
|
* {@link #startOp started}. The tracked data can only be read by system components.
|
|
*
|
|
* <p><b>Only {@link #noteOp}/{@link #startOp} are tracked; {@link #unsafeCheckOp} is not tracked.
|
|
* Hence it is important to eventually call {@link #noteOp} or {@link #startOp} when providing
|
|
* access to protected operations or data.</b>
|
|
*
|
|
* <p>Some apps are forwarding access to other apps. E.g. an app might get the location from the
|
|
* system's location provider and then send the location further to a 3rd app. In this case the
|
|
* app passing on the data needs to call {@link #noteProxyOp} to signal the access proxying. This
|
|
* might also make sense inside of a single app if the access is forwarded between two parts of
|
|
* the tagged with different attribution tags.
|
|
*
|
|
* <p>An app can register an {@link OnOpNotedCallback} to get informed about what accesses the
|
|
* system is tracking for it. As each runtime permission has an associated app-op this API is
|
|
* particularly useful for an app that want to find unexpected private data accesses.
|
|
*/
|
|
@SystemService(Context.APP_OPS_SERVICE)
|
|
public class AppOpsManager {
|
|
/**
|
|
* This is a subtle behavior change to {@link #startWatchingMode}.
|
|
*
|
|
* Before this change the system called back for the switched op. After the change the system
|
|
* will call back for the actually requested op or all switched ops if no op is specified.
|
|
*
|
|
* @hide
|
|
*/
|
|
@ChangeId
|
|
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
|
|
public static final long CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE = 148180766L;
|
|
|
|
/**
|
|
* Enforce that all attributionTags send to {@link #noteOp}, {@link #noteProxyOp},
|
|
* and {@link #startOp} are defined in the manifest of the package that is specified as
|
|
* parameter to the methods.
|
|
*
|
|
* <p>To enable this change both the package calling {@link #noteOp} as well as the package
|
|
* specified as parameter to the method need to have this change enable.
|
|
*
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
@ChangeId
|
|
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
|
|
public static final long SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE = 151105954L;
|
|
|
|
private static final String FULL_LOG = "privacy_attribution_tag_full_log_enabled";
|
|
|
|
private static final int MAX_UNFORWARDED_OPS = 10;
|
|
|
|
private static Boolean sFullLog = null;
|
|
|
|
final Context mContext;
|
|
private PermissionUsageHelper mUsageHelper;
|
|
|
|
@UnsupportedAppUsage
|
|
final IAppOpsService mService;
|
|
|
|
/**
|
|
* Service for the application context, to be used by static methods via
|
|
* {@link #getService()}
|
|
*/
|
|
@GuardedBy("sLock")
|
|
static IAppOpsService sService;
|
|
|
|
@GuardedBy("mModeWatchers")
|
|
private final ArrayMap<OnOpChangedListener, IAppOpsCallback> mModeWatchers =
|
|
new ArrayMap<>();
|
|
|
|
@GuardedBy("mActiveWatchers")
|
|
private final ArrayMap<OnOpActiveChangedListener, IAppOpsActiveCallback> mActiveWatchers =
|
|
new ArrayMap<>();
|
|
|
|
@GuardedBy("mStartedWatchers")
|
|
private final ArrayMap<OnOpStartedListener, IAppOpsStartedCallback> mStartedWatchers =
|
|
new ArrayMap<>();
|
|
|
|
@GuardedBy("mNotedWatchers")
|
|
private final ArrayMap<OnOpNotedListener, IAppOpsNotedCallback> mNotedWatchers =
|
|
new ArrayMap<>();
|
|
|
|
private static final Object sLock = new Object();
|
|
|
|
/** Current {@link OnOpNotedCallback}. Change via {@link #setOnOpNotedCallback} */
|
|
@GuardedBy("sLock")
|
|
private static @Nullable OnOpNotedCallback sOnOpNotedCallback;
|
|
|
|
/**
|
|
* Sync note-ops collected from {@link #readAndLogNotedAppops(Parcel)} that have not been
|
|
* delivered to a callback yet.
|
|
*
|
|
* Similar to {@link com.android.server.appop.AppOpsService#mUnforwardedAsyncNotedOps} for
|
|
* {@link COLLECT_ASYNC}. Used in situation when AppOpsManager asks to collect stacktrace with
|
|
* {@link #sMessageCollector}, which forces {@link COLLECT_SYNC} mode.
|
|
*/
|
|
@GuardedBy("sLock")
|
|
private static ArrayList<AsyncNotedAppOp> sUnforwardedOps = new ArrayList<>();
|
|
|
|
/**
|
|
* Additional collector that collect accesses and forwards a few of them them via
|
|
* {@link IAppOpsService#reportRuntimeAppOpAccessMessageAndGetConfig}.
|
|
*/
|
|
private static OnOpNotedCallback sMessageCollector =
|
|
new OnOpNotedCallback() {
|
|
@Override
|
|
public void onNoted(@NonNull SyncNotedAppOp op) {
|
|
reportStackTraceIfNeeded(op);
|
|
}
|
|
|
|
@Override
|
|
public void onAsyncNoted(@NonNull AsyncNotedAppOp asyncOp) {
|
|
// collected directly in AppOpsService
|
|
}
|
|
|
|
@Override
|
|
public void onSelfNoted(@NonNull SyncNotedAppOp op) {
|
|
reportStackTraceIfNeeded(op);
|
|
}
|
|
|
|
private void reportStackTraceIfNeeded(@NonNull SyncNotedAppOp op) {
|
|
if (!isCollectingStackTraces()) {
|
|
return;
|
|
}
|
|
MessageSamplingConfig config = sConfig;
|
|
if (leftCircularDistance(strOpToOp(op.getOp()), config.getSampledOpCode(),
|
|
_NUM_OP) <= config.getAcceptableLeftDistance()
|
|
|| config.getExpirationTimeSinceBootMillis()
|
|
< SystemClock.elapsedRealtime()) {
|
|
String stackTrace = getFormattedStackTrace();
|
|
try {
|
|
String packageName = ActivityThread.currentOpPackageName();
|
|
sConfig = getService().reportRuntimeAppOpAccessMessageAndGetConfig(
|
|
packageName == null ? "" : packageName, op, stackTrace);
|
|
} catch (RemoteException e) {
|
|
e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
static IBinder sClientId;
|
|
|
|
/**
|
|
* How many seconds we want for a drop in uid state from top to settle before applying it.
|
|
*
|
|
* <>Set a parameter to {@link android.provider.Settings.Global#APP_OPS_CONSTANTS}
|
|
*
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
public static final String KEY_TOP_STATE_SETTLE_TIME = "top_state_settle_time";
|
|
|
|
/**
|
|
* How many second we want for a drop in uid state from foreground to settle before applying it.
|
|
*
|
|
* <>Set a parameter to {@link android.provider.Settings.Global#APP_OPS_CONSTANTS}
|
|
*
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
public static final String KEY_FG_SERVICE_STATE_SETTLE_TIME =
|
|
"fg_service_state_settle_time";
|
|
|
|
/**
|
|
* How many seconds we want for a drop in uid state from background to settle before applying
|
|
* it.
|
|
*
|
|
* <>Set a parameter to {@link android.provider.Settings.Global#APP_OPS_CONSTANTS}
|
|
*
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
public static final String KEY_BG_STATE_SETTLE_TIME = "bg_state_settle_time";
|
|
|
|
/** @hide */
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@IntDef(flag = true, prefix = { "HISTORICAL_MODE_" }, value = {
|
|
HISTORICAL_MODE_DISABLED,
|
|
HISTORICAL_MODE_ENABLED_ACTIVE,
|
|
HISTORICAL_MODE_ENABLED_PASSIVE
|
|
})
|
|
public @interface HistoricalMode {}
|
|
|
|
/**
|
|
* Mode in which app op history is completely disabled.
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
public static final int HISTORICAL_MODE_DISABLED = 0;
|
|
|
|
/**
|
|
* Mode in which app op history is enabled and app ops performed by apps would
|
|
* be tracked. This is the mode in which the feature is completely enabled.
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
public static final int HISTORICAL_MODE_ENABLED_ACTIVE = 1;
|
|
|
|
/**
|
|
* Mode in which app op history is enabled but app ops performed by apps would
|
|
* not be tracked and the only way to add ops to the history is via explicit calls
|
|
* to dedicated APIs. This mode is useful for testing to allow full control of
|
|
* the historical content.
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
public static final int HISTORICAL_MODE_ENABLED_PASSIVE = 2;
|
|
|
|
/** @hide */
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@IntDef(prefix = { "MODE_" }, value = {
|
|
MODE_ALLOWED,
|
|
MODE_IGNORED,
|
|
MODE_ERRORED,
|
|
MODE_DEFAULT,
|
|
MODE_FOREGROUND
|
|
})
|
|
public @interface Mode {}
|
|
|
|
/**
|
|
* Result from {@link #checkOp}, {@link #noteOp}, {@link #startOp}: the given caller is
|
|
* allowed to perform the given operation.
|
|
*/
|
|
public static final int MODE_ALLOWED = 0;
|
|
|
|
/**
|
|
* Result from {@link #checkOp}, {@link #noteOp}, {@link #startOp}: the given caller is
|
|
* not allowed to perform the given operation, and this attempt should
|
|
* <em>silently fail</em> (it should not cause the app to crash).
|
|
*/
|
|
public static final int MODE_IGNORED = 1;
|
|
|
|
/**
|
|
* Result from {@link #checkOpNoThrow}, {@link #noteOpNoThrow}, {@link #startOpNoThrow}: the
|
|
* given caller is not allowed to perform the given operation, and this attempt should
|
|
* cause it to have a fatal error, typically a {@link SecurityException}.
|
|
*/
|
|
public static final int MODE_ERRORED = 2;
|
|
|
|
/**
|
|
* Result from {@link #checkOp}, {@link #noteOp}, {@link #startOp}: the given caller should
|
|
* use its default security check. This mode is not normally used; it should only be used
|
|
* with appop permissions, and callers must explicitly check for it and deal with it.
|
|
*/
|
|
public static final int MODE_DEFAULT = 3;
|
|
|
|
/**
|
|
* Special mode that means "allow only when app is in foreground." This is <b>not</b>
|
|
* returned from {@link #unsafeCheckOp}, {@link #noteOp}, {@link #startOp}. Rather,
|
|
* {@link #unsafeCheckOp} will always return {@link #MODE_ALLOWED} (because it is always
|
|
* possible for it to be ultimately allowed, depending on the app's background state),
|
|
* and {@link #noteOp} and {@link #startOp} will return {@link #MODE_ALLOWED} when the app
|
|
* being checked is currently in the foreground, otherwise {@link #MODE_IGNORED}.
|
|
*
|
|
* <p>The only place you will this normally see this value is through
|
|
* {@link #unsafeCheckOpRaw}, which returns the actual raw mode of the op. Note that because
|
|
* you can't know the current state of the app being checked (and it can change at any
|
|
* point), you can only treat the result here as an indication that it will vary between
|
|
* {@link #MODE_ALLOWED} and {@link #MODE_IGNORED} depending on changes in the background
|
|
* state of the app. You thus must always use {@link #noteOp} or {@link #startOp} to do
|
|
* the actual check for access to the op.</p>
|
|
*/
|
|
public static final int MODE_FOREGROUND = 4;
|
|
|
|
/**
|
|
* Flag for {@link #startWatchingMode(String, String, int, OnOpChangedListener)}:
|
|
* Also get reports if the foreground state of an op's uid changes. This only works
|
|
* when watching a particular op, not when watching a package.
|
|
*/
|
|
public static final int WATCH_FOREGROUND_CHANGES = 1 << 0;
|
|
|
|
/**
|
|
* Flag for {@link #startWatchingMode} that causes the callback to happen on the switch-op
|
|
* instead the op the callback was registered. (This simulates pre-R behavior).
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int CALL_BACK_ON_SWITCHED_OP = 1 << 1;
|
|
|
|
/**
|
|
* Flag to determine whether we should log noteOp/startOp calls to make sure they
|
|
* are correctly used
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final boolean NOTE_OP_COLLECTION_ENABLED = false;
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
public static final String[] MODE_NAMES = new String[] {
|
|
"allow", // MODE_ALLOWED
|
|
"ignore", // MODE_IGNORED
|
|
"deny", // MODE_ERRORED
|
|
"default", // MODE_DEFAULT
|
|
"foreground", // MODE_FOREGROUND
|
|
};
|
|
|
|
/** @hide */
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@IntDef(prefix = { "UID_STATE_" }, value = {
|
|
UID_STATE_PERSISTENT,
|
|
UID_STATE_TOP,
|
|
UID_STATE_FOREGROUND_SERVICE_LOCATION,
|
|
UID_STATE_FOREGROUND_SERVICE,
|
|
UID_STATE_FOREGROUND,
|
|
UID_STATE_BACKGROUND,
|
|
UID_STATE_CACHED,
|
|
UID_STATE_NONEXISTENT
|
|
})
|
|
public @interface UidState {}
|
|
|
|
/**
|
|
* Uid state: The UID is a foreground persistent app. The lower the UID
|
|
* state the more important the UID is for the user.
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public static final int UID_STATE_PERSISTENT = 100;
|
|
|
|
/**
|
|
* Uid state: The UID is top foreground app. The lower the UID
|
|
* state the more important the UID is for the user.
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public static final int UID_STATE_TOP = 200;
|
|
|
|
/**
|
|
* Uid state: The UID is running a foreground service of location type.
|
|
* The lower the UID state the more important the UID is for the user.
|
|
* This uid state is a counterpart to PROCESS_STATE_FOREGROUND_SERVICE_LOCATION which has been
|
|
* deprecated.
|
|
* @hide
|
|
* @deprecated
|
|
*/
|
|
@SystemApi
|
|
@Deprecated
|
|
public static final int UID_STATE_FOREGROUND_SERVICE_LOCATION = 300;
|
|
|
|
/**
|
|
* Uid state: The UID is running a foreground service. The lower the UID
|
|
* state the more important the UID is for the user.
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public static final int UID_STATE_FOREGROUND_SERVICE = 400;
|
|
|
|
/**
|
|
* Uid state: The UID is a foreground app. The lower the UID
|
|
* state the more important the UID is for the user.
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public static final int UID_STATE_FOREGROUND = 500;
|
|
|
|
/**
|
|
* The max, which is min priority, UID state for which any app op
|
|
* would be considered as performed in the foreground.
|
|
* @hide
|
|
*/
|
|
public static final int UID_STATE_MAX_LAST_NON_RESTRICTED = UID_STATE_FOREGROUND;
|
|
|
|
/**
|
|
* Uid state: The UID is a background app. The lower the UID
|
|
* state the more important the UID is for the user.
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public static final int UID_STATE_BACKGROUND = 600;
|
|
|
|
/**
|
|
* Uid state: The UID is a cached app. The lower the UID
|
|
* state the more important the UID is for the user.
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public static final int UID_STATE_CACHED = 700;
|
|
|
|
/**
|
|
* Uid state: The UID state with the highest priority.
|
|
* @hide
|
|
*/
|
|
public static final int MAX_PRIORITY_UID_STATE = UID_STATE_PERSISTENT;
|
|
|
|
/**
|
|
* Uid state: The UID state with the lowest priority.
|
|
* @hide
|
|
*/
|
|
public static final int MIN_PRIORITY_UID_STATE = UID_STATE_CACHED;
|
|
|
|
/**
|
|
* Special uid state: The UID is not running
|
|
* @hide
|
|
*/
|
|
public static final int UID_STATE_NONEXISTENT = Integer.MAX_VALUE;
|
|
|
|
/**
|
|
* Resolves the first unrestricted state given an app op.
|
|
* @param op The op to resolve.
|
|
* @return The last restricted UID state.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static int resolveFirstUnrestrictedUidState(int op) {
|
|
return UID_STATE_MAX_LAST_NON_RESTRICTED;
|
|
}
|
|
|
|
/**
|
|
* Resolves the last restricted state given an app op.
|
|
* @param op The op to resolve.
|
|
* @return The last restricted UID state.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static int resolveLastRestrictedUidState(int op) {
|
|
return UID_STATE_BACKGROUND;
|
|
}
|
|
|
|
/** @hide Note: Keep these sorted */
|
|
public static final int[] UID_STATES = {
|
|
UID_STATE_PERSISTENT,
|
|
UID_STATE_TOP,
|
|
UID_STATE_FOREGROUND_SERVICE_LOCATION,
|
|
UID_STATE_FOREGROUND_SERVICE,
|
|
UID_STATE_FOREGROUND,
|
|
UID_STATE_BACKGROUND,
|
|
UID_STATE_CACHED
|
|
// UID_STATE_NONEXISTENT isn't a real UID state, so it is excluded
|
|
};
|
|
|
|
/** @hide */
|
|
public static String getUidStateName(@UidState int uidState) {
|
|
switch (uidState) {
|
|
case UID_STATE_PERSISTENT:
|
|
return "pers";
|
|
case UID_STATE_TOP:
|
|
return "top";
|
|
case UID_STATE_FOREGROUND_SERVICE_LOCATION:
|
|
return "fgsvcl";
|
|
case UID_STATE_FOREGROUND_SERVICE:
|
|
return "fgsvc";
|
|
case UID_STATE_FOREGROUND:
|
|
return "fg";
|
|
case UID_STATE_BACKGROUND:
|
|
return "bg";
|
|
case UID_STATE_CACHED:
|
|
return "cch";
|
|
case UID_STATE_NONEXISTENT:
|
|
return "gone";
|
|
default:
|
|
return "unknown";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Flag: non proxy operations. These are operations
|
|
* performed on behalf of the app itself and not on behalf of
|
|
* another one.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public static final int OP_FLAG_SELF = 0x1;
|
|
|
|
/**
|
|
* Flag: trusted proxy operations. These are operations
|
|
* performed on behalf of another app by a trusted app.
|
|
* Which is work a trusted app blames on another app.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public static final int OP_FLAG_TRUSTED_PROXY = 0x2;
|
|
|
|
/**
|
|
* Flag: untrusted proxy operations. These are operations
|
|
* performed on behalf of another app by an untrusted app.
|
|
* Which is work an untrusted app blames on another app.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public static final int OP_FLAG_UNTRUSTED_PROXY = 0x4;
|
|
|
|
/**
|
|
* Flag: trusted proxied operations. These are operations
|
|
* performed by a trusted other app on behalf of an app.
|
|
* Which is work an app was blamed for by a trusted app.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public static final int OP_FLAG_TRUSTED_PROXIED = 0x8;
|
|
|
|
/**
|
|
* Flag: untrusted proxied operations. These are operations
|
|
* performed by an untrusted other app on behalf of an app.
|
|
* Which is work an app was blamed for by an untrusted app.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public static final int OP_FLAG_UNTRUSTED_PROXIED = 0x10;
|
|
|
|
/**
|
|
* Flags: all operations. These include operations matched
|
|
* by {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXIED},
|
|
* {@link #OP_FLAG_UNTRUSTED_PROXIED}, {@link #OP_FLAG_TRUSTED_PROXIED},
|
|
* {@link #OP_FLAG_UNTRUSTED_PROXIED}.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public static final int OP_FLAGS_ALL =
|
|
OP_FLAG_SELF
|
|
| OP_FLAG_TRUSTED_PROXY
|
|
| OP_FLAG_UNTRUSTED_PROXY
|
|
| OP_FLAG_TRUSTED_PROXIED
|
|
| OP_FLAG_UNTRUSTED_PROXIED;
|
|
|
|
/**
|
|
* Flags: all trusted operations which is ones either the app did {@link #OP_FLAG_SELF},
|
|
* or it was blamed for by a trusted app {@link #OP_FLAG_TRUSTED_PROXIED}, or ones the
|
|
* app if untrusted blamed on other apps {@link #OP_FLAG_UNTRUSTED_PROXY}.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public static final int OP_FLAGS_ALL_TRUSTED = AppOpsManager.OP_FLAG_SELF
|
|
| AppOpsManager.OP_FLAG_UNTRUSTED_PROXY
|
|
| AppOpsManager.OP_FLAG_TRUSTED_PROXIED;
|
|
|
|
/** @hide */
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@IntDef(flag = true, prefix = { "FLAG_" }, value = {
|
|
OP_FLAG_SELF,
|
|
OP_FLAG_TRUSTED_PROXY,
|
|
OP_FLAG_UNTRUSTED_PROXY,
|
|
OP_FLAG_TRUSTED_PROXIED,
|
|
OP_FLAG_UNTRUSTED_PROXIED
|
|
})
|
|
public @interface OpFlags {}
|
|
|
|
/** @hide */
|
|
public static final String getFlagName(@OpFlags int flag) {
|
|
switch (flag) {
|
|
case OP_FLAG_SELF:
|
|
return "s";
|
|
case OP_FLAG_TRUSTED_PROXY:
|
|
return "tp";
|
|
case OP_FLAG_UNTRUSTED_PROXY:
|
|
return "up";
|
|
case OP_FLAG_TRUSTED_PROXIED:
|
|
return "tpd";
|
|
case OP_FLAG_UNTRUSTED_PROXIED:
|
|
return "upd";
|
|
default:
|
|
return "unknown";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Attribution chain flag: specifies that this is the accessor. When
|
|
* an app A accesses the data that is then passed to app B that is then
|
|
* passed to C, we call app A accessor, app B intermediary, and app C
|
|
* receiver. If A accesses the data for itself, then it is the accessor
|
|
* and the receiver.
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
public static final int ATTRIBUTION_FLAG_ACCESSOR = 0x1;
|
|
|
|
/**
|
|
* Attribution chain flag: specifies that this is the intermediary. When
|
|
* an app A accesses the data that is then passed to app B that is then
|
|
* passed to C, we call app A accessor, app B intermediary, and app C
|
|
* receiver. If A accesses the data for itself, then it is the accessor
|
|
* and the receiver.
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
public static final int ATTRIBUTION_FLAG_INTERMEDIARY = 0x2;
|
|
|
|
/**
|
|
* Attribution chain flag: specifies that this is the receiver. When
|
|
* an app A accesses the data that is then passed to app B that is then
|
|
* passed to C, we call app A accessor, app B intermediary, and app C
|
|
* receiver. If A accesses the data for itself, then it is the accessor
|
|
* and the receiver.
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
public static final int ATTRIBUTION_FLAG_RECEIVER = 0x4;
|
|
|
|
/**
|
|
* Attribution chain flag: Specifies that all attribution sources in the chain were trusted.
|
|
* Must only be set by system server.
|
|
* @hide
|
|
*/
|
|
public static final int ATTRIBUTION_FLAG_TRUSTED = 0x8;
|
|
|
|
/**
|
|
* No attribution flags.
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
public static final int ATTRIBUTION_FLAGS_NONE = 0x0;
|
|
|
|
/**
|
|
* No attribution chain id.
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
public static final int ATTRIBUTION_CHAIN_ID_NONE = -1;
|
|
|
|
/** @hide */
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@IntDef(flag = true, prefix = { "FLAG_" }, value = {
|
|
ATTRIBUTION_FLAG_ACCESSOR,
|
|
ATTRIBUTION_FLAG_INTERMEDIARY,
|
|
ATTRIBUTION_FLAG_RECEIVER,
|
|
ATTRIBUTION_FLAG_TRUSTED
|
|
})
|
|
public @interface AttributionFlags {}
|
|
|
|
// These constants are redefined here to work around a metalava limitation/bug where
|
|
// @IntDef is not able to see @hide symbols when they are hidden via package hiding:
|
|
// frameworks/base/core/java/com/android/internal/package.html
|
|
|
|
/** @hide */
|
|
public static final int SAMPLING_STRATEGY_DEFAULT =
|
|
FrameworkStatsLog.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__DEFAULT;
|
|
|
|
/** @hide */
|
|
public static final int SAMPLING_STRATEGY_UNIFORM =
|
|
FrameworkStatsLog.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__UNIFORM;
|
|
|
|
/** @hide */
|
|
public static final int SAMPLING_STRATEGY_RARELY_USED =
|
|
FrameworkStatsLog.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__RARELY_USED;
|
|
|
|
/** @hide */
|
|
public static final int SAMPLING_STRATEGY_BOOT_TIME_SAMPLING =
|
|
FrameworkStatsLog.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__BOOT_TIME_SAMPLING;
|
|
|
|
/** @hide */
|
|
public static final int SAMPLING_STRATEGY_UNIFORM_OPS =
|
|
FrameworkStatsLog.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__UNIFORM_OPS;
|
|
|
|
/**
|
|
* Strategies used for message sampling
|
|
* @hide
|
|
*/
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@IntDef(prefix = {"SAMPLING_STRATEGY_"}, value = {
|
|
SAMPLING_STRATEGY_DEFAULT,
|
|
SAMPLING_STRATEGY_UNIFORM,
|
|
SAMPLING_STRATEGY_RARELY_USED,
|
|
SAMPLING_STRATEGY_BOOT_TIME_SAMPLING,
|
|
SAMPLING_STRATEGY_UNIFORM_OPS
|
|
})
|
|
public @interface SamplingStrategy {}
|
|
|
|
private static final int UID_STATE_OFFSET = 31;
|
|
private static final int FLAGS_MASK = 0xFFFFFFFF;
|
|
|
|
/**
|
|
* Key for a data bucket storing app op state. The bucket
|
|
* is composed of the uid state and state flags. This way
|
|
* we can query data for given uid state and a set of flags where
|
|
* the flags control which type of data to get. For example,
|
|
* one can get the ops an app did on behalf of other apps
|
|
* while in the background.
|
|
*
|
|
* @hide
|
|
*/
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
|
|
public @interface DataBucketKey {
|
|
}
|
|
|
|
/** @hide */
|
|
public static String keyToString(@DataBucketKey long key) {
|
|
final int uidState = extractUidStateFromKey(key);
|
|
final int flags = extractFlagsFromKey(key);
|
|
return "[" + getUidStateName(uidState) + "-" + flagsToString(flags) + "]";
|
|
}
|
|
|
|
/** @hide */
|
|
public static @DataBucketKey long makeKey(@UidState int uidState, @OpFlags int flags) {
|
|
return ((long) uidState << UID_STATE_OFFSET) | flags;
|
|
}
|
|
|
|
/** @hide */
|
|
public static int extractUidStateFromKey(@DataBucketKey long key) {
|
|
return (int) (key >> UID_STATE_OFFSET);
|
|
}
|
|
|
|
/** @hide */
|
|
public static int extractFlagsFromKey(@DataBucketKey long key) {
|
|
return (int) (key & FLAGS_MASK);
|
|
}
|
|
|
|
/** @hide */
|
|
public static String flagsToString(@OpFlags int flags) {
|
|
final StringBuilder flagsBuilder = new StringBuilder();
|
|
while (flags != 0) {
|
|
final int flag = 1 << Integer.numberOfTrailingZeros(flags);
|
|
flags &= ~flag;
|
|
if (flagsBuilder.length() > 0) {
|
|
flagsBuilder.append('|');
|
|
}
|
|
flagsBuilder.append(getFlagName(flag));
|
|
}
|
|
return flagsBuilder.toString();
|
|
}
|
|
|
|
// when adding one of these:
|
|
// - increment _NUM_OP
|
|
// - define an OPSTR_* constant (and mark as @SystemApi if needed)
|
|
// - add row to sAppOpInfos
|
|
// - add descriptive strings to Settings/res/values/arrays.xml
|
|
// - add the op to the appropriate template in AppOpsState.OpsTemplate (settings app)
|
|
|
|
/** @hide No operation specified. */
|
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
|
public static final int OP_NONE = AppProtoEnums.APP_OP_NONE;
|
|
/** @hide Access to coarse location information. */
|
|
@UnsupportedAppUsage
|
|
@TestApi
|
|
public static final int OP_COARSE_LOCATION = AppProtoEnums.APP_OP_COARSE_LOCATION;
|
|
/** @hide Access to fine location information. */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_FINE_LOCATION = AppProtoEnums.APP_OP_FINE_LOCATION;
|
|
/** @hide Causing GPS to run. */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_GPS = AppProtoEnums.APP_OP_GPS;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_VIBRATE = AppProtoEnums.APP_OP_VIBRATE;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_READ_CONTACTS = AppProtoEnums.APP_OP_READ_CONTACTS;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_WRITE_CONTACTS = AppProtoEnums.APP_OP_WRITE_CONTACTS;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_READ_CALL_LOG = AppProtoEnums.APP_OP_READ_CALL_LOG;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_WRITE_CALL_LOG = AppProtoEnums.APP_OP_WRITE_CALL_LOG;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_READ_CALENDAR = AppProtoEnums.APP_OP_READ_CALENDAR;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_WRITE_CALENDAR = AppProtoEnums.APP_OP_WRITE_CALENDAR;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_WIFI_SCAN = AppProtoEnums.APP_OP_WIFI_SCAN;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_POST_NOTIFICATION = AppProtoEnums.APP_OP_POST_NOTIFICATION;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_NEIGHBORING_CELLS = AppProtoEnums.APP_OP_NEIGHBORING_CELLS;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_CALL_PHONE = AppProtoEnums.APP_OP_CALL_PHONE;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_READ_SMS = AppProtoEnums.APP_OP_READ_SMS;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_WRITE_SMS = AppProtoEnums.APP_OP_WRITE_SMS;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_RECEIVE_SMS = AppProtoEnums.APP_OP_RECEIVE_SMS;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_RECEIVE_EMERGECY_SMS =
|
|
AppProtoEnums.APP_OP_RECEIVE_EMERGENCY_SMS;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_RECEIVE_MMS = AppProtoEnums.APP_OP_RECEIVE_MMS;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_RECEIVE_WAP_PUSH = AppProtoEnums.APP_OP_RECEIVE_WAP_PUSH;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_SEND_SMS = AppProtoEnums.APP_OP_SEND_SMS;
|
|
/** @hide */
|
|
public static final int OP_MANAGE_ONGOING_CALLS = AppProtoEnums.APP_OP_MANAGE_ONGOING_CALLS;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_READ_ICC_SMS = AppProtoEnums.APP_OP_READ_ICC_SMS;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_WRITE_ICC_SMS = AppProtoEnums.APP_OP_WRITE_ICC_SMS;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_WRITE_SETTINGS = AppProtoEnums.APP_OP_WRITE_SETTINGS;
|
|
/** @hide Required to draw on top of other apps. */
|
|
@UnsupportedAppUsage
|
|
@TestApi
|
|
public static final int OP_SYSTEM_ALERT_WINDOW = AppProtoEnums.APP_OP_SYSTEM_ALERT_WINDOW;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_ACCESS_NOTIFICATIONS =
|
|
AppProtoEnums.APP_OP_ACCESS_NOTIFICATIONS;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_CAMERA = AppProtoEnums.APP_OP_CAMERA;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
@TestApi
|
|
public static final int OP_RECORD_AUDIO = AppProtoEnums.APP_OP_RECORD_AUDIO;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_PLAY_AUDIO = AppProtoEnums.APP_OP_PLAY_AUDIO;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_READ_CLIPBOARD = AppProtoEnums.APP_OP_READ_CLIPBOARD;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_WRITE_CLIPBOARD = AppProtoEnums.APP_OP_WRITE_CLIPBOARD;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_TAKE_MEDIA_BUTTONS = AppProtoEnums.APP_OP_TAKE_MEDIA_BUTTONS;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_TAKE_AUDIO_FOCUS = AppProtoEnums.APP_OP_TAKE_AUDIO_FOCUS;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_AUDIO_MASTER_VOLUME = AppProtoEnums.APP_OP_AUDIO_MASTER_VOLUME;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_AUDIO_VOICE_VOLUME = AppProtoEnums.APP_OP_AUDIO_VOICE_VOLUME;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_AUDIO_RING_VOLUME = AppProtoEnums.APP_OP_AUDIO_RING_VOLUME;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_AUDIO_MEDIA_VOLUME = AppProtoEnums.APP_OP_AUDIO_MEDIA_VOLUME;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_AUDIO_ALARM_VOLUME = AppProtoEnums.APP_OP_AUDIO_ALARM_VOLUME;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_AUDIO_NOTIFICATION_VOLUME =
|
|
AppProtoEnums.APP_OP_AUDIO_NOTIFICATION_VOLUME;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_AUDIO_BLUETOOTH_VOLUME =
|
|
AppProtoEnums.APP_OP_AUDIO_BLUETOOTH_VOLUME;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_WAKE_LOCK = AppProtoEnums.APP_OP_WAKE_LOCK;
|
|
/** @hide Continually monitoring location data. */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_MONITOR_LOCATION =
|
|
AppProtoEnums.APP_OP_MONITOR_LOCATION;
|
|
/** @hide Continually monitoring location data with a relatively high power request. */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_MONITOR_HIGH_POWER_LOCATION =
|
|
AppProtoEnums.APP_OP_MONITOR_HIGH_POWER_LOCATION;
|
|
/** @hide Retrieve current usage stats via {@link UsageStatsManager}. */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_GET_USAGE_STATS = AppProtoEnums.APP_OP_GET_USAGE_STATS;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_MUTE_MICROPHONE = AppProtoEnums.APP_OP_MUTE_MICROPHONE;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_TOAST_WINDOW = AppProtoEnums.APP_OP_TOAST_WINDOW;
|
|
/** @hide Capture the device's display contents and/or audio */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_PROJECT_MEDIA = AppProtoEnums.APP_OP_PROJECT_MEDIA;
|
|
/**
|
|
* Start (without additional user intervention) a VPN connection, as used by {@link
|
|
* android.net.VpnService} along with as Platform VPN connections, as used by {@link
|
|
* android.net.VpnManager}
|
|
*
|
|
* <p>This appop is granted to apps that have already been given user consent to start
|
|
* VpnService based VPN connections. As this is a superset of OP_ACTIVATE_PLATFORM_VPN, this
|
|
* appop also allows the starting of Platform VPNs.
|
|
*
|
|
* @hide
|
|
*/
|
|
@UnsupportedAppUsage
|
|
public static final int OP_ACTIVATE_VPN = AppProtoEnums.APP_OP_ACTIVATE_VPN;
|
|
/** @hide Access the WallpaperManagerAPI to write wallpapers. */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_WRITE_WALLPAPER = AppProtoEnums.APP_OP_WRITE_WALLPAPER;
|
|
/** @hide Received the assist structure from an app. */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_ASSIST_STRUCTURE = AppProtoEnums.APP_OP_ASSIST_STRUCTURE;
|
|
/** @hide Received a screenshot from assist. */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_ASSIST_SCREENSHOT = AppProtoEnums.APP_OP_ASSIST_SCREENSHOT;
|
|
/** @hide Read the phone state. */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_READ_PHONE_STATE = AppProtoEnums.APP_OP_READ_PHONE_STATE;
|
|
/** @hide Add voicemail messages to the voicemail content provider. */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_ADD_VOICEMAIL = AppProtoEnums.APP_OP_ADD_VOICEMAIL;
|
|
/** @hide Access APIs for SIP calling over VOIP or WiFi. */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_USE_SIP = AppProtoEnums.APP_OP_USE_SIP;
|
|
/** @hide Intercept outgoing calls. */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_PROCESS_OUTGOING_CALLS =
|
|
AppProtoEnums.APP_OP_PROCESS_OUTGOING_CALLS;
|
|
/** @hide User the fingerprint API. */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_USE_FINGERPRINT = AppProtoEnums.APP_OP_USE_FINGERPRINT;
|
|
/** @hide Access to body sensors such as heart rate, etc. */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_BODY_SENSORS = AppProtoEnums.APP_OP_BODY_SENSORS;
|
|
/** @hide Read previously received cell broadcast messages. */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_READ_CELL_BROADCASTS = AppProtoEnums.APP_OP_READ_CELL_BROADCASTS;
|
|
/** @hide Inject mock location into the system. */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_MOCK_LOCATION = AppProtoEnums.APP_OP_MOCK_LOCATION;
|
|
/** @hide Read external storage. */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_READ_EXTERNAL_STORAGE = AppProtoEnums.APP_OP_READ_EXTERNAL_STORAGE;
|
|
/** @hide Write external storage. */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_WRITE_EXTERNAL_STORAGE =
|
|
AppProtoEnums.APP_OP_WRITE_EXTERNAL_STORAGE;
|
|
/** @hide Turned on the screen. */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_TURN_SCREEN_ON = AppProtoEnums.APP_OP_TURN_SCREEN_ON;
|
|
/** @hide Get device accounts. */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_GET_ACCOUNTS = AppProtoEnums.APP_OP_GET_ACCOUNTS;
|
|
/** @hide Control whether an application is allowed to run in the background. */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_RUN_IN_BACKGROUND =
|
|
AppProtoEnums.APP_OP_RUN_IN_BACKGROUND;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_AUDIO_ACCESSIBILITY_VOLUME =
|
|
AppProtoEnums.APP_OP_AUDIO_ACCESSIBILITY_VOLUME;
|
|
/** @hide Read the phone number. */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_READ_PHONE_NUMBERS = AppProtoEnums.APP_OP_READ_PHONE_NUMBERS;
|
|
/** @hide Request package installs through package installer */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_REQUEST_INSTALL_PACKAGES =
|
|
AppProtoEnums.APP_OP_REQUEST_INSTALL_PACKAGES;
|
|
/** @hide Enter picture-in-picture. */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_PICTURE_IN_PICTURE = AppProtoEnums.APP_OP_PICTURE_IN_PICTURE;
|
|
/** @hide Instant app start foreground service. */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_INSTANT_APP_START_FOREGROUND =
|
|
AppProtoEnums.APP_OP_INSTANT_APP_START_FOREGROUND;
|
|
/** @hide Answer incoming phone calls */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_ANSWER_PHONE_CALLS = AppProtoEnums.APP_OP_ANSWER_PHONE_CALLS;
|
|
/** @hide Run jobs when in background */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_RUN_ANY_IN_BACKGROUND = AppProtoEnums.APP_OP_RUN_ANY_IN_BACKGROUND;
|
|
/** @hide Change Wi-Fi connectivity state */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_CHANGE_WIFI_STATE = AppProtoEnums.APP_OP_CHANGE_WIFI_STATE;
|
|
/** @hide Request package deletion through package installer */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_REQUEST_DELETE_PACKAGES =
|
|
AppProtoEnums.APP_OP_REQUEST_DELETE_PACKAGES;
|
|
/** @hide Bind an accessibility service. */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_BIND_ACCESSIBILITY_SERVICE =
|
|
AppProtoEnums.APP_OP_BIND_ACCESSIBILITY_SERVICE;
|
|
/** @hide Continue handover of a call from another app */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_ACCEPT_HANDOVER = AppProtoEnums.APP_OP_ACCEPT_HANDOVER;
|
|
/** @hide Create and Manage IPsec Tunnels */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_MANAGE_IPSEC_TUNNELS = AppProtoEnums.APP_OP_MANAGE_IPSEC_TUNNELS;
|
|
/** @hide Any app start foreground service. */
|
|
@UnsupportedAppUsage
|
|
@TestApi
|
|
public static final int OP_START_FOREGROUND = AppProtoEnums.APP_OP_START_FOREGROUND;
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
public static final int OP_BLUETOOTH_SCAN = AppProtoEnums.APP_OP_BLUETOOTH_SCAN;
|
|
/** @hide */
|
|
public static final int OP_BLUETOOTH_CONNECT = AppProtoEnums.APP_OP_BLUETOOTH_CONNECT;
|
|
/** @hide */
|
|
public static final int OP_BLUETOOTH_ADVERTISE = AppProtoEnums.APP_OP_BLUETOOTH_ADVERTISE;
|
|
/** @hide Use the BiometricPrompt/BiometricManager APIs. */
|
|
public static final int OP_USE_BIOMETRIC = AppProtoEnums.APP_OP_USE_BIOMETRIC;
|
|
/** @hide Physical activity recognition. */
|
|
public static final int OP_ACTIVITY_RECOGNITION = AppProtoEnums.APP_OP_ACTIVITY_RECOGNITION;
|
|
/** @hide Financial app sms read. */
|
|
public static final int OP_SMS_FINANCIAL_TRANSACTIONS =
|
|
AppProtoEnums.APP_OP_SMS_FINANCIAL_TRANSACTIONS;
|
|
/** @hide Read media of audio type. */
|
|
public static final int OP_READ_MEDIA_AUDIO = AppProtoEnums.APP_OP_READ_MEDIA_AUDIO;
|
|
/** @hide Write media of audio type. */
|
|
public static final int OP_WRITE_MEDIA_AUDIO = AppProtoEnums.APP_OP_WRITE_MEDIA_AUDIO;
|
|
/** @hide Read media of video type. */
|
|
public static final int OP_READ_MEDIA_VIDEO = AppProtoEnums.APP_OP_READ_MEDIA_VIDEO;
|
|
/** @hide Write media of video type. */
|
|
public static final int OP_WRITE_MEDIA_VIDEO = AppProtoEnums.APP_OP_WRITE_MEDIA_VIDEO;
|
|
/** @hide Read media of image type. */
|
|
public static final int OP_READ_MEDIA_IMAGES = AppProtoEnums.APP_OP_READ_MEDIA_IMAGES;
|
|
/** @hide Write media of image type. */
|
|
public static final int OP_WRITE_MEDIA_IMAGES = AppProtoEnums.APP_OP_WRITE_MEDIA_IMAGES;
|
|
/** @hide Has a legacy (non-isolated) view of storage. */
|
|
public static final int OP_LEGACY_STORAGE = AppProtoEnums.APP_OP_LEGACY_STORAGE;
|
|
/** @hide Accessing accessibility features */
|
|
public static final int OP_ACCESS_ACCESSIBILITY = AppProtoEnums.APP_OP_ACCESS_ACCESSIBILITY;
|
|
/** @hide Read the device identifiers (IMEI / MEID, IMSI, SIM / Build serial) */
|
|
public static final int OP_READ_DEVICE_IDENTIFIERS =
|
|
AppProtoEnums.APP_OP_READ_DEVICE_IDENTIFIERS;
|
|
/** @hide Read location metadata from media */
|
|
public static final int OP_ACCESS_MEDIA_LOCATION = AppProtoEnums.APP_OP_ACCESS_MEDIA_LOCATION;
|
|
/** @hide Query all apps on device, regardless of declarations in the calling app manifest */
|
|
public static final int OP_QUERY_ALL_PACKAGES = AppProtoEnums.APP_OP_QUERY_ALL_PACKAGES;
|
|
/** @hide Access all external storage */
|
|
public static final int OP_MANAGE_EXTERNAL_STORAGE =
|
|
AppProtoEnums.APP_OP_MANAGE_EXTERNAL_STORAGE;
|
|
/** @hide Communicate cross-profile within the same profile group. */
|
|
public static final int OP_INTERACT_ACROSS_PROFILES =
|
|
AppProtoEnums.APP_OP_INTERACT_ACROSS_PROFILES;
|
|
/**
|
|
* Start (without additional user intervention) a Platform VPN connection, as used by {@link
|
|
* android.net.VpnManager}
|
|
*
|
|
* <p>This appop is granted to apps that have already been given user consent to start Platform
|
|
* VPN connections. This appop is insufficient to start VpnService based VPNs; OP_ACTIVATE_VPN
|
|
* is needed for that.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_ACTIVATE_PLATFORM_VPN = AppProtoEnums.APP_OP_ACTIVATE_PLATFORM_VPN;
|
|
/** @hide Controls whether or not read logs are available for incremental installations. */
|
|
public static final int OP_LOADER_USAGE_STATS = AppProtoEnums.APP_OP_LOADER_USAGE_STATS;
|
|
|
|
// App op deprecated/removed.
|
|
private static final int OP_DEPRECATED_1 = AppProtoEnums.APP_OP_DEPRECATED_1;
|
|
|
|
/** @hide Auto-revoke app permissions if app is unused for an extended period */
|
|
public static final int OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED =
|
|
AppProtoEnums.APP_OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED;
|
|
|
|
/**
|
|
* Whether {@link #OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED} is allowed to be changed by
|
|
* the installer
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_AUTO_REVOKE_MANAGED_BY_INSTALLER =
|
|
AppProtoEnums.APP_OP_AUTO_REVOKE_MANAGED_BY_INSTALLER;
|
|
|
|
/** @hide */
|
|
public static final int OP_NO_ISOLATED_STORAGE = AppProtoEnums.APP_OP_NO_ISOLATED_STORAGE;
|
|
|
|
/**
|
|
* Phone call is using microphone
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_PHONE_CALL_MICROPHONE = AppProtoEnums.APP_OP_PHONE_CALL_MICROPHONE;
|
|
/**
|
|
* Phone call is using camera
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_PHONE_CALL_CAMERA = AppProtoEnums.APP_OP_PHONE_CALL_CAMERA;
|
|
|
|
/**
|
|
* Audio is being recorded for hotword detection.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_RECORD_AUDIO_HOTWORD = AppProtoEnums.APP_OP_RECORD_AUDIO_HOTWORD;
|
|
|
|
/**
|
|
* Manage credentials in the system KeyChain.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_MANAGE_CREDENTIALS = AppProtoEnums.APP_OP_MANAGE_CREDENTIALS;
|
|
|
|
/** @hide */
|
|
public static final int OP_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER =
|
|
AppProtoEnums.APP_OP_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER;
|
|
|
|
/**
|
|
* App output audio is being recorded
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_RECORD_AUDIO_OUTPUT = AppProtoEnums.APP_OP_RECORD_AUDIO_OUTPUT;
|
|
|
|
/**
|
|
* App can schedule exact alarm to perform timing based background work
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_SCHEDULE_EXACT_ALARM = AppProtoEnums.APP_OP_SCHEDULE_EXACT_ALARM;
|
|
|
|
/**
|
|
* Fine location being accessed by a location source, which is
|
|
* a component that already has location data since it is the one
|
|
* that produces location, which is it is a data source for
|
|
* location data.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_FINE_LOCATION_SOURCE = AppProtoEnums.APP_OP_FINE_LOCATION_SOURCE;
|
|
|
|
/**
|
|
* Coarse location being accessed by a location source, which is
|
|
* a component that already has location data since it is the one
|
|
* that produces location, which is it is a data source for
|
|
* location data.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_COARSE_LOCATION_SOURCE = AppProtoEnums.APP_OP_COARSE_LOCATION_SOURCE;
|
|
|
|
/**
|
|
* Allow apps to create the requests to manage the media files without user confirmation.
|
|
*
|
|
* @see android.Manifest.permission#MANAGE_MEDIA
|
|
* @see android.provider.MediaStore#createDeleteRequest(ContentResolver, Collection)
|
|
* @see android.provider.MediaStore#createTrashRequest(ContentResolver, Collection, boolean)
|
|
* @see android.provider.MediaStore#createWriteRequest(ContentResolver, Collection)
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_MANAGE_MEDIA = AppProtoEnums.APP_OP_MANAGE_MEDIA;
|
|
|
|
/** @hide */
|
|
public static final int OP_UWB_RANGING = AppProtoEnums.APP_OP_UWB_RANGING;
|
|
|
|
/** @hide */
|
|
public static final int OP_NEARBY_WIFI_DEVICES = AppProtoEnums.APP_OP_NEARBY_WIFI_DEVICES;
|
|
|
|
/**
|
|
* Activity recognition being accessed by an activity recognition source, which
|
|
* is a component that already has access since it is the one that detects
|
|
* activity recognition.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_ACTIVITY_RECOGNITION_SOURCE =
|
|
AppProtoEnums.APP_OP_ACTIVITY_RECOGNITION_SOURCE;
|
|
|
|
/**
|
|
* Incoming phone audio is being recorded
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_RECORD_INCOMING_PHONE_AUDIO =
|
|
AppProtoEnums.APP_OP_RECORD_INCOMING_PHONE_AUDIO;
|
|
|
|
/**
|
|
* VPN app establishes a connection through the VpnService API.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_ESTABLISH_VPN_SERVICE = AppProtoEnums.APP_OP_ESTABLISH_VPN_SERVICE;
|
|
|
|
/**
|
|
* VPN app establishes a connection through the VpnManager API.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_ESTABLISH_VPN_MANAGER = AppProtoEnums.APP_OP_ESTABLISH_VPN_MANAGER;
|
|
|
|
/**
|
|
* Access restricted settings.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_ACCESS_RESTRICTED_SETTINGS =
|
|
AppProtoEnums.APP_OP_ACCESS_RESTRICTED_SETTINGS;
|
|
|
|
/**
|
|
* Receive microphone audio from an ambient sound detection event
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_RECEIVE_AMBIENT_TRIGGER_AUDIO =
|
|
AppProtoEnums.APP_OP_RECEIVE_AMBIENT_TRIGGER_AUDIO;
|
|
|
|
/**
|
|
* Receive audio from near-field mic (ie. TV remote)
|
|
* Allows audio recording regardless of sensor privacy state,
|
|
* as it is an intentional user interaction: hold-to-talk
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO =
|
|
AppProtoEnums.APP_OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO;
|
|
|
|
/**
|
|
* App can schedule user-initiated jobs.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_RUN_USER_INITIATED_JOBS =
|
|
AppProtoEnums.APP_OP_RUN_USER_INITIATED_JOBS;
|
|
|
|
/**
|
|
* Notify apps that they have been granted URI permission photos
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_READ_MEDIA_VISUAL_USER_SELECTED =
|
|
AppProtoEnums.APP_OP_READ_MEDIA_VISUAL_USER_SELECTED;
|
|
|
|
/**
|
|
* Prevent an app from being suspended.
|
|
*
|
|
* Only to be used by the system.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_SYSTEM_EXEMPT_FROM_SUSPENSION =
|
|
AppProtoEnums.APP_OP_SYSTEM_EXEMPT_FROM_SUSPENSION;
|
|
|
|
/**
|
|
* Prevent an app from dismissible notifications. Starting from Android U, notifications with
|
|
* the ongoing parameter can be dismissed by a user on an unlocked device. An app with
|
|
* this appop will be exempt and cannot be dismissed by a user.
|
|
*
|
|
* Only to be used by the system.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS =
|
|
AppProtoEnums.APP_OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS;
|
|
|
|
/**
|
|
* An app op for reading/writing health connect data.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_READ_WRITE_HEALTH_DATA = AppProtoEnums.APP_OP_READ_WRITE_HEALTH_DATA;
|
|
|
|
/**
|
|
* Use foreground service with the type
|
|
* {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_SPECIAL_USE}.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_FOREGROUND_SERVICE_SPECIAL_USE =
|
|
AppProtoEnums.APP_OP_FOREGROUND_SERVICE_SPECIAL_USE;
|
|
|
|
/**
|
|
* Exempt an app from all power-related restrictions, including app standby and doze.
|
|
* In addition, the app will be able to start foreground services from the background, and the
|
|
* user will not be able to stop foreground services run by the app.
|
|
*
|
|
* Only to be used by the system.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS =
|
|
AppProtoEnums.APP_OP_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS;
|
|
|
|
/**
|
|
* Prevent an app from being placed into hibernation.
|
|
*
|
|
* Only to be used by the system.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_SYSTEM_EXEMPT_FROM_HIBERNATION =
|
|
AppProtoEnums.APP_OP_SYSTEM_EXEMPT_FROM_HIBERNATION;
|
|
|
|
/**
|
|
* Allows an application to start an activity while running in the background.
|
|
*
|
|
* Only to be used by the system.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION =
|
|
AppProtoEnums.APP_OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION;
|
|
|
|
/**
|
|
* Allows an application to capture bugreport directly without consent dialog when using the
|
|
* bugreporting API on userdebug/eng build.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD =
|
|
AppProtoEnums.APP_OP_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD;
|
|
|
|
// App op deprecated/removed.
|
|
private static final int OP_DEPRECATED_2 = AppProtoEnums.APP_OP_BODY_SENSORS_WRIST_TEMPERATURE;
|
|
|
|
/**
|
|
* Send an intent to launch instead of posting the notification to the status bar.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_USE_FULL_SCREEN_INTENT = AppProtoEnums.APP_OP_USE_FULL_SCREEN_INTENT;
|
|
|
|
/**
|
|
* Hides camera indicator for sandboxed detection apps that directly access the service.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_CAMERA_SANDBOXED =
|
|
AppProtoEnums.APP_OP_CAMERA_SANDBOXED;
|
|
|
|
/**
|
|
* Hides microphone indicator for sandboxed detection apps that directly access the service.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_RECORD_AUDIO_SANDBOXED =
|
|
AppProtoEnums.APP_OP_RECORD_AUDIO_SANDBOXED;
|
|
|
|
/**
|
|
* Allows the assistant app to be voice-triggered by detected hotwords from a trusted detection
|
|
* service.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_RECEIVE_SANDBOX_TRIGGER_AUDIO =
|
|
AppProtoEnums.APP_OP_RECEIVE_SANDBOX_TRIGGER_AUDIO;
|
|
|
|
/**
|
|
* This op has been deprecated.
|
|
*
|
|
*/
|
|
private static final int OP_DEPRECATED_3 =
|
|
AppProtoEnums.APP_OP_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA;
|
|
|
|
/**
|
|
* Creation of an overlay using accessibility services
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_CREATE_ACCESSIBILITY_OVERLAY =
|
|
AppProtoEnums.APP_OP_CREATE_ACCESSIBILITY_OVERLAY;
|
|
|
|
/**
|
|
* Indicate that the user has enabled or disabled mobile data
|
|
* @hide
|
|
*/
|
|
public static final int OP_ENABLE_MOBILE_DATA_BY_USER =
|
|
AppProtoEnums.APP_OP_ENABLE_MOBILE_DATA_BY_USER;
|
|
|
|
/**
|
|
* See {@link #OPSTR_MEDIA_ROUTING_CONTROL}.
|
|
* @hide
|
|
*/
|
|
public static final int OP_MEDIA_ROUTING_CONTROL = AppProtoEnums.APP_OP_MEDIA_ROUTING_CONTROL;
|
|
|
|
/**
|
|
* Op code for use by tests to avoid interfering history logs that the wider system might
|
|
* trigger.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_RESERVED_FOR_TESTING = AppProtoEnums.APP_OP_RESERVED_FOR_TESTING;
|
|
|
|
/**
|
|
* Rapid clearing of notifications by a notification listener
|
|
*
|
|
* @hide
|
|
*/
|
|
// See b/289080543 for more details
|
|
public static final int OP_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER =
|
|
AppProtoEnums.APP_OP_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER;
|
|
|
|
/**
|
|
* See {@link #OPSTR_READ_SYSTEM_GRAMMATICAL_GENDER}.
|
|
* @hide
|
|
*/
|
|
public static final int OP_READ_SYSTEM_GRAMMATICAL_GENDER =
|
|
AppProtoEnums.APP_OP_READ_SYSTEM_GRAMMATICAL_GENDER;
|
|
|
|
/**
|
|
* This app has been removed..
|
|
*
|
|
* @hide
|
|
*/
|
|
private static final int OP_DEPRECATED_4 = AppProtoEnums.APP_OP_RUN_BACKUP_JOBS;
|
|
|
|
/**
|
|
* Whether the app has enabled to receive the icon overlay for fetching archived apps.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_ARCHIVE_ICON_OVERLAY = AppProtoEnums.APP_OP_ARCHIVE_ICON_OVERLAY;
|
|
|
|
/**
|
|
* Whether the app has enabled compatibility support for unarchival.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_UNARCHIVAL_CONFIRMATION =
|
|
AppProtoEnums.APP_OP_UNARCHIVAL_CONFIRMATION;
|
|
|
|
/**
|
|
* Allows an app to access location without the traditional location permissions and while the
|
|
* user location setting is off, but only during pre-defined emergency sessions.
|
|
*
|
|
* <p>This op is only used for tracking, not for permissions, so it is still the client's
|
|
* responsibility to check the {@link Manifest.permission.LOCATION_BYPASS} permission
|
|
* appropriately.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int OP_EMERGENCY_LOCATION = AppProtoEnums.APP_OP_EMERGENCY_LOCATION;
|
|
|
|
/** @hide */
|
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
|
public static final int _NUM_OP = 148;
|
|
|
|
/**
|
|
* All app ops represented as strings.
|
|
*
|
|
* @hide
|
|
*/
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@StringDef(prefix = { "OPSTR_" }, value = {
|
|
OPSTR_COARSE_LOCATION,
|
|
OPSTR_FINE_LOCATION,
|
|
OPSTR_MONITOR_LOCATION,
|
|
OPSTR_MONITOR_HIGH_POWER_LOCATION,
|
|
OPSTR_GET_USAGE_STATS,
|
|
OPSTR_ACTIVATE_VPN,
|
|
OPSTR_READ_CONTACTS,
|
|
OPSTR_WRITE_CONTACTS,
|
|
OPSTR_READ_CALL_LOG,
|
|
OPSTR_WRITE_CALL_LOG,
|
|
OPSTR_READ_CALENDAR,
|
|
OPSTR_WRITE_CALENDAR,
|
|
OPSTR_CALL_PHONE,
|
|
OPSTR_READ_SMS,
|
|
OPSTR_RECEIVE_SMS,
|
|
OPSTR_RECEIVE_MMS,
|
|
OPSTR_RECEIVE_WAP_PUSH,
|
|
OPSTR_SEND_SMS,
|
|
OPSTR_CAMERA,
|
|
OPSTR_RECORD_AUDIO,
|
|
OPSTR_READ_PHONE_STATE,
|
|
OPSTR_ADD_VOICEMAIL,
|
|
OPSTR_USE_SIP,
|
|
OPSTR_PROCESS_OUTGOING_CALLS,
|
|
OPSTR_USE_FINGERPRINT,
|
|
OPSTR_BODY_SENSORS,
|
|
OPSTR_READ_CELL_BROADCASTS,
|
|
OPSTR_MOCK_LOCATION,
|
|
OPSTR_READ_EXTERNAL_STORAGE,
|
|
OPSTR_WRITE_EXTERNAL_STORAGE,
|
|
OPSTR_SYSTEM_ALERT_WINDOW,
|
|
OPSTR_WRITE_SETTINGS,
|
|
OPSTR_GET_ACCOUNTS,
|
|
OPSTR_READ_PHONE_NUMBERS,
|
|
OPSTR_PICTURE_IN_PICTURE,
|
|
OPSTR_INSTANT_APP_START_FOREGROUND,
|
|
OPSTR_ANSWER_PHONE_CALLS,
|
|
OPSTR_ACCEPT_HANDOVER,
|
|
OPSTR_GPS,
|
|
OPSTR_VIBRATE,
|
|
OPSTR_WIFI_SCAN,
|
|
OPSTR_POST_NOTIFICATION,
|
|
OPSTR_NEIGHBORING_CELLS,
|
|
OPSTR_WRITE_SMS,
|
|
OPSTR_RECEIVE_EMERGENCY_BROADCAST,
|
|
OPSTR_READ_ICC_SMS,
|
|
OPSTR_WRITE_ICC_SMS,
|
|
OPSTR_ACCESS_NOTIFICATIONS,
|
|
OPSTR_PLAY_AUDIO,
|
|
OPSTR_READ_CLIPBOARD,
|
|
OPSTR_WRITE_CLIPBOARD,
|
|
OPSTR_TAKE_MEDIA_BUTTONS,
|
|
OPSTR_TAKE_AUDIO_FOCUS,
|
|
OPSTR_AUDIO_MASTER_VOLUME,
|
|
OPSTR_AUDIO_VOICE_VOLUME,
|
|
OPSTR_AUDIO_RING_VOLUME,
|
|
OPSTR_AUDIO_MEDIA_VOLUME,
|
|
OPSTR_AUDIO_ALARM_VOLUME,
|
|
OPSTR_AUDIO_NOTIFICATION_VOLUME,
|
|
OPSTR_AUDIO_BLUETOOTH_VOLUME,
|
|
OPSTR_WAKE_LOCK,
|
|
OPSTR_MUTE_MICROPHONE,
|
|
OPSTR_TOAST_WINDOW,
|
|
OPSTR_PROJECT_MEDIA,
|
|
OPSTR_WRITE_WALLPAPER,
|
|
OPSTR_ASSIST_STRUCTURE,
|
|
OPSTR_ASSIST_SCREENSHOT,
|
|
OPSTR_TURN_SCREEN_ON,
|
|
OPSTR_RUN_IN_BACKGROUND,
|
|
OPSTR_AUDIO_ACCESSIBILITY_VOLUME,
|
|
OPSTR_REQUEST_INSTALL_PACKAGES,
|
|
OPSTR_RUN_ANY_IN_BACKGROUND,
|
|
OPSTR_CHANGE_WIFI_STATE,
|
|
OPSTR_REQUEST_DELETE_PACKAGES,
|
|
OPSTR_BIND_ACCESSIBILITY_SERVICE,
|
|
OPSTR_MANAGE_IPSEC_TUNNELS,
|
|
OPSTR_START_FOREGROUND,
|
|
OPSTR_BLUETOOTH_SCAN,
|
|
OPSTR_BLUETOOTH_CONNECT,
|
|
OPSTR_BLUETOOTH_ADVERTISE,
|
|
OPSTR_USE_BIOMETRIC,
|
|
OPSTR_ACTIVITY_RECOGNITION,
|
|
OPSTR_SMS_FINANCIAL_TRANSACTIONS,
|
|
OPSTR_READ_MEDIA_AUDIO,
|
|
OPSTR_WRITE_MEDIA_AUDIO,
|
|
OPSTR_READ_MEDIA_VIDEO,
|
|
OPSTR_WRITE_MEDIA_VIDEO,
|
|
OPSTR_READ_MEDIA_IMAGES,
|
|
OPSTR_WRITE_MEDIA_IMAGES,
|
|
OPSTR_LEGACY_STORAGE,
|
|
OPSTR_ACCESS_MEDIA_LOCATION,
|
|
OPSTR_ACCESS_ACCESSIBILITY,
|
|
OPSTR_READ_DEVICE_IDENTIFIERS,
|
|
OPSTR_QUERY_ALL_PACKAGES,
|
|
OPSTR_MANAGE_EXTERNAL_STORAGE,
|
|
OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
|
|
OPSTR_AUTO_REVOKE_MANAGED_BY_INSTALLER,
|
|
OPSTR_INTERACT_ACROSS_PROFILES,
|
|
OPSTR_ACTIVATE_PLATFORM_VPN,
|
|
OPSTR_LOADER_USAGE_STATS,
|
|
OPSTR_MANAGE_ONGOING_CALLS,
|
|
OPSTR_NO_ISOLATED_STORAGE,
|
|
OPSTR_PHONE_CALL_MICROPHONE,
|
|
OPSTR_PHONE_CALL_CAMERA,
|
|
OPSTR_RECORD_AUDIO_HOTWORD,
|
|
OPSTR_MANAGE_CREDENTIALS,
|
|
OPSTR_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER,
|
|
OPSTR_RECORD_AUDIO_OUTPUT,
|
|
OPSTR_SCHEDULE_EXACT_ALARM,
|
|
OPSTR_FINE_LOCATION_SOURCE,
|
|
OPSTR_COARSE_LOCATION_SOURCE,
|
|
OPSTR_MANAGE_MEDIA,
|
|
OPSTR_UWB_RANGING,
|
|
OPSTR_NEARBY_WIFI_DEVICES,
|
|
OPSTR_ACTIVITY_RECOGNITION_SOURCE,
|
|
OPSTR_RECORD_INCOMING_PHONE_AUDIO,
|
|
OPSTR_ESTABLISH_VPN_SERVICE,
|
|
OPSTR_ESTABLISH_VPN_MANAGER,
|
|
OPSTR_ACCESS_RESTRICTED_SETTINGS,
|
|
OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO,
|
|
OPSTR_READ_MEDIA_VISUAL_USER_SELECTED,
|
|
OPSTR_READ_WRITE_HEALTH_DATA,
|
|
OPSTR_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO,
|
|
OPSTR_RUN_USER_INITIATED_JOBS,
|
|
OPSTR_SYSTEM_EXEMPT_FROM_SUSPENSION,
|
|
OPSTR_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS,
|
|
OPSTR_FOREGROUND_SERVICE_SPECIAL_USE,
|
|
OPSTR_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS,
|
|
OPSTR_SYSTEM_EXEMPT_FROM_HIBERNATION,
|
|
OPSTR_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION,
|
|
OPSTR_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD,
|
|
OPSTR_USE_FULL_SCREEN_INTENT,
|
|
OPSTR_CAMERA_SANDBOXED,
|
|
OPSTR_RECORD_AUDIO_SANDBOXED,
|
|
OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO,
|
|
OPSTR_CREATE_ACCESSIBILITY_OVERLAY,
|
|
OPSTR_MEDIA_ROUTING_CONTROL,
|
|
OPSTR_ENABLE_MOBILE_DATA_BY_USER,
|
|
OPSTR_RESERVED_FOR_TESTING,
|
|
OPSTR_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER,
|
|
OPSTR_ARCHIVE_ICON_OVERLAY,
|
|
OPSTR_UNARCHIVAL_CONFIRMATION,
|
|
OPSTR_EMERGENCY_LOCATION,
|
|
})
|
|
public @interface AppOpString {}
|
|
|
|
/** Access to coarse location information. */
|
|
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
|
|
/** Access to fine location information. */
|
|
public static final String OPSTR_FINE_LOCATION =
|
|
"android:fine_location";
|
|
/** Continually monitoring location data. */
|
|
public static final String OPSTR_MONITOR_LOCATION
|
|
= "android:monitor_location";
|
|
/** Continually monitoring location data with a relatively high power request. */
|
|
public static final String OPSTR_MONITOR_HIGH_POWER_LOCATION
|
|
= "android:monitor_location_high_power";
|
|
/** Access to {@link android.app.usage.UsageStatsManager}. */
|
|
public static final String OPSTR_GET_USAGE_STATS
|
|
= "android:get_usage_stats";
|
|
/** Activate a VPN connection without user intervention. @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_ACTIVATE_VPN
|
|
= "android:activate_vpn";
|
|
/** Allows an application to read the user's contacts data. */
|
|
public static final String OPSTR_READ_CONTACTS
|
|
= "android:read_contacts";
|
|
/** Allows an application to write to the user's contacts data. */
|
|
public static final String OPSTR_WRITE_CONTACTS
|
|
= "android:write_contacts";
|
|
/** Allows an application to read the user's call log. */
|
|
public static final String OPSTR_READ_CALL_LOG
|
|
= "android:read_call_log";
|
|
/** Allows an application to write to the user's call log. */
|
|
public static final String OPSTR_WRITE_CALL_LOG
|
|
= "android:write_call_log";
|
|
/** Allows an application to read the user's calendar data. */
|
|
public static final String OPSTR_READ_CALENDAR
|
|
= "android:read_calendar";
|
|
/** Allows an application to write to the user's calendar data. */
|
|
public static final String OPSTR_WRITE_CALENDAR
|
|
= "android:write_calendar";
|
|
/** Allows an application to initiate a phone call. */
|
|
public static final String OPSTR_CALL_PHONE
|
|
= "android:call_phone";
|
|
/** Allows an application to read SMS messages. */
|
|
public static final String OPSTR_READ_SMS
|
|
= "android:read_sms";
|
|
/** Allows an application to receive SMS messages. */
|
|
public static final String OPSTR_RECEIVE_SMS
|
|
= "android:receive_sms";
|
|
/** Allows an application to receive MMS messages. */
|
|
public static final String OPSTR_RECEIVE_MMS
|
|
= "android:receive_mms";
|
|
/** Allows an application to receive WAP push messages. */
|
|
public static final String OPSTR_RECEIVE_WAP_PUSH
|
|
= "android:receive_wap_push";
|
|
/** Allows an application to send SMS messages. */
|
|
public static final String OPSTR_SEND_SMS
|
|
= "android:send_sms";
|
|
/** Required to be able to access the camera device. */
|
|
public static final String OPSTR_CAMERA
|
|
= "android:camera";
|
|
/** Required to be able to access the microphone device. */
|
|
public static final String OPSTR_RECORD_AUDIO
|
|
= "android:record_audio";
|
|
/** Required to access phone state related information. */
|
|
public static final String OPSTR_READ_PHONE_STATE
|
|
= "android:read_phone_state";
|
|
/** Required to access phone state related information. */
|
|
public static final String OPSTR_ADD_VOICEMAIL
|
|
= "android:add_voicemail";
|
|
/** Access APIs for SIP calling over VOIP or WiFi */
|
|
public static final String OPSTR_USE_SIP
|
|
= "android:use_sip";
|
|
/** Access APIs for diverting outgoing calls */
|
|
public static final String OPSTR_PROCESS_OUTGOING_CALLS
|
|
= "android:process_outgoing_calls";
|
|
/** Use the fingerprint API. */
|
|
public static final String OPSTR_USE_FINGERPRINT
|
|
= "android:use_fingerprint";
|
|
/** Access to body sensors such as heart rate, etc. */
|
|
public static final String OPSTR_BODY_SENSORS
|
|
= "android:body_sensors";
|
|
/** Read previously received cell broadcast messages. */
|
|
public static final String OPSTR_READ_CELL_BROADCASTS
|
|
= "android:read_cell_broadcasts";
|
|
/** Inject mock location into the system. */
|
|
public static final String OPSTR_MOCK_LOCATION
|
|
= "android:mock_location";
|
|
/** Read external storage. */
|
|
public static final String OPSTR_READ_EXTERNAL_STORAGE
|
|
= "android:read_external_storage";
|
|
/** Write external storage. */
|
|
public static final String OPSTR_WRITE_EXTERNAL_STORAGE
|
|
= "android:write_external_storage";
|
|
/** Required to draw on top of other apps. */
|
|
public static final String OPSTR_SYSTEM_ALERT_WINDOW
|
|
= "android:system_alert_window";
|
|
/** Required to write/modify/update system settings. */
|
|
public static final String OPSTR_WRITE_SETTINGS
|
|
= "android:write_settings";
|
|
/** @hide Get device accounts. */
|
|
@SystemApi
|
|
public static final String OPSTR_GET_ACCOUNTS
|
|
= "android:get_accounts";
|
|
public static final String OPSTR_READ_PHONE_NUMBERS
|
|
= "android:read_phone_numbers";
|
|
/** Access to picture-in-picture. */
|
|
public static final String OPSTR_PICTURE_IN_PICTURE
|
|
= "android:picture_in_picture";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_INSTANT_APP_START_FOREGROUND
|
|
= "android:instant_app_start_foreground";
|
|
/** Answer incoming phone calls */
|
|
public static final String OPSTR_ANSWER_PHONE_CALLS
|
|
= "android:answer_phone_calls";
|
|
/**
|
|
* Accept call handover
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public static final String OPSTR_ACCEPT_HANDOVER
|
|
= "android:accept_handover";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_GPS = "android:gps";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_VIBRATE = "android:vibrate";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_WIFI_SCAN = "android:wifi_scan";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_POST_NOTIFICATION = "android:post_notification";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_NEIGHBORING_CELLS = "android:neighboring_cells";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_WRITE_SMS = "android:write_sms";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_RECEIVE_EMERGENCY_BROADCAST =
|
|
"android:receive_emergency_broadcast";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_READ_ICC_SMS = "android:read_icc_sms";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_WRITE_ICC_SMS = "android:write_icc_sms";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_ACCESS_NOTIFICATIONS = "android:access_notifications";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_PLAY_AUDIO = "android:play_audio";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_READ_CLIPBOARD = "android:read_clipboard";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_WRITE_CLIPBOARD = "android:write_clipboard";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_TAKE_MEDIA_BUTTONS = "android:take_media_buttons";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_TAKE_AUDIO_FOCUS = "android:take_audio_focus";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_AUDIO_MASTER_VOLUME = "android:audio_master_volume";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_AUDIO_VOICE_VOLUME = "android:audio_voice_volume";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_AUDIO_RING_VOLUME = "android:audio_ring_volume";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_AUDIO_MEDIA_VOLUME = "android:audio_media_volume";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_AUDIO_ALARM_VOLUME = "android:audio_alarm_volume";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_AUDIO_NOTIFICATION_VOLUME =
|
|
"android:audio_notification_volume";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_AUDIO_BLUETOOTH_VOLUME = "android:audio_bluetooth_volume";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_WAKE_LOCK = "android:wake_lock";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_MUTE_MICROPHONE = "android:mute_microphone";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_TOAST_WINDOW = "android:toast_window";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_PROJECT_MEDIA = "android:project_media";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_WRITE_WALLPAPER = "android:write_wallpaper";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_ASSIST_STRUCTURE = "android:assist_structure";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_ASSIST_SCREENSHOT = "android:assist_screenshot";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_TURN_SCREEN_ON = "android:turn_screen_on";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_RUN_IN_BACKGROUND = "android:run_in_background";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_AUDIO_ACCESSIBILITY_VOLUME =
|
|
"android:audio_accessibility_volume";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_REQUEST_INSTALL_PACKAGES = "android:request_install_packages";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_RUN_ANY_IN_BACKGROUND = "android:run_any_in_background";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_CHANGE_WIFI_STATE = "android:change_wifi_state";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_REQUEST_DELETE_PACKAGES = "android:request_delete_packages";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_BIND_ACCESSIBILITY_SERVICE =
|
|
"android:bind_accessibility_service";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_MANAGE_IPSEC_TUNNELS = "android:manage_ipsec_tunnels";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_START_FOREGROUND = "android:start_foreground";
|
|
/** @hide */
|
|
public static final String OPSTR_BLUETOOTH_SCAN = "android:bluetooth_scan";
|
|
/** @hide */
|
|
public static final String OPSTR_BLUETOOTH_CONNECT = "android:bluetooth_connect";
|
|
/** @hide */
|
|
public static final String OPSTR_BLUETOOTH_ADVERTISE = "android:bluetooth_advertise";
|
|
|
|
/** @hide Use the BiometricPrompt/BiometricManager APIs. */
|
|
public static final String OPSTR_USE_BIOMETRIC = "android:use_biometric";
|
|
|
|
/** @hide Recognize physical activity. */
|
|
@TestApi
|
|
public static final String OPSTR_ACTIVITY_RECOGNITION = "android:activity_recognition";
|
|
|
|
/** @hide Financial app read sms. */
|
|
public static final String OPSTR_SMS_FINANCIAL_TRANSACTIONS =
|
|
"android:sms_financial_transactions";
|
|
|
|
/** @hide Read media of audio type. */
|
|
@SystemApi
|
|
public static final String OPSTR_READ_MEDIA_AUDIO = "android:read_media_audio";
|
|
/** @hide Write media of audio type. */
|
|
@SystemApi
|
|
public static final String OPSTR_WRITE_MEDIA_AUDIO = "android:write_media_audio";
|
|
/** @hide Read media of video type. */
|
|
@SystemApi
|
|
public static final String OPSTR_READ_MEDIA_VIDEO = "android:read_media_video";
|
|
/** @hide Write media of video type. */
|
|
@SystemApi
|
|
public static final String OPSTR_WRITE_MEDIA_VIDEO = "android:write_media_video";
|
|
/** @hide Read media of image type. */
|
|
@SystemApi
|
|
public static final String OPSTR_READ_MEDIA_IMAGES = "android:read_media_images";
|
|
/** @hide Write media of image type. */
|
|
@SystemApi
|
|
public static final String OPSTR_WRITE_MEDIA_IMAGES = "android:write_media_images";
|
|
/** @hide Has a legacy (non-isolated) view of storage. */
|
|
@SystemApi
|
|
public static final String OPSTR_LEGACY_STORAGE = "android:legacy_storage";
|
|
/** @hide Read location metadata from media */
|
|
public static final String OPSTR_ACCESS_MEDIA_LOCATION = "android:access_media_location";
|
|
|
|
/** @hide Interact with accessibility. */
|
|
@SystemApi
|
|
public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility";
|
|
/** @hide Read device identifiers */
|
|
public static final String OPSTR_READ_DEVICE_IDENTIFIERS = "android:read_device_identifiers";
|
|
/** @hide Query all packages on device */
|
|
public static final String OPSTR_QUERY_ALL_PACKAGES = "android:query_all_packages";
|
|
/** @hide Access all external storage */
|
|
@SystemApi
|
|
public static final String OPSTR_MANAGE_EXTERNAL_STORAGE =
|
|
"android:manage_external_storage";
|
|
|
|
/** @hide Auto-revoke app permissions if app is unused for an extended period */
|
|
@SystemApi
|
|
public static final String OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED =
|
|
"android:auto_revoke_permissions_if_unused";
|
|
|
|
/** @hide Auto-revoke app permissions if app is unused for an extended period */
|
|
@SystemApi
|
|
public static final String OPSTR_AUTO_REVOKE_MANAGED_BY_INSTALLER =
|
|
"android:auto_revoke_managed_by_installer";
|
|
|
|
/** @hide Communicate cross-profile within the same profile group. */
|
|
@SystemApi
|
|
public static final String OPSTR_INTERACT_ACROSS_PROFILES = "android:interact_across_profiles";
|
|
/** @hide Start Platform VPN without user intervention */
|
|
@SystemApi
|
|
public static final String OPSTR_ACTIVATE_PLATFORM_VPN = "android:activate_platform_vpn";
|
|
/** @hide */
|
|
@SystemApi
|
|
public static final String OPSTR_LOADER_USAGE_STATS = "android:loader_usage_stats";
|
|
|
|
/**
|
|
* Grants an app access to the {@link android.telecom.InCallService} API to see
|
|
* information about ongoing calls and to enable control of calls.
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
@TestApi
|
|
public static final String OPSTR_MANAGE_ONGOING_CALLS = "android:manage_ongoing_calls";
|
|
|
|
/**
|
|
* Allows apps holding this permission to control the routing of other apps via {@link
|
|
* MediaRouter2}.
|
|
*
|
|
* <p>For example, holding this permission allows watches (via companion apps) to control the
|
|
* routing of applications running on the phone.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
@FlaggedApi(com.android.media.flags.Flags
|
|
.FLAG_ENABLE_PRIVILEGED_ROUTING_FOR_MEDIA_ROUTING_CONTROL)
|
|
public static final String OPSTR_MEDIA_ROUTING_CONTROL = "android:media_routing_control";
|
|
|
|
/**
|
|
* Whether the app has enabled to receive the icon overlay for fetching archived apps.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final String OPSTR_ARCHIVE_ICON_OVERLAY = "android:archive_icon_overlay";
|
|
|
|
/**
|
|
* Whether the app has enabled compatibility support for unarchival.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final String OPSTR_UNARCHIVAL_CONFIRMATION = "android:unarchival_support";
|
|
|
|
/**
|
|
* AppOp granted to apps that we are started via {@code am instrument -e --no-isolated-storage}
|
|
*
|
|
* <p>MediaProvider is the only component (outside of system server) that should care about this
|
|
* app op, hence {@code SystemApi.Client.MODULE_LIBRARIES}.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
|
|
public static final String OPSTR_NO_ISOLATED_STORAGE = "android:no_isolated_storage";
|
|
|
|
/**
|
|
* Phone call is using microphone
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public static final String OPSTR_PHONE_CALL_MICROPHONE = "android:phone_call_microphone";
|
|
/**
|
|
* Phone call is using camera
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public static final String OPSTR_PHONE_CALL_CAMERA = "android:phone_call_camera";
|
|
|
|
/**
|
|
* Audio is being recorded for hotword detection.
|
|
*
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
public static final String OPSTR_RECORD_AUDIO_HOTWORD = "android:record_audio_hotword";
|
|
|
|
/**
|
|
* Manage credentials in the system KeyChain.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final String OPSTR_MANAGE_CREDENTIALS = "android:manage_credentials";
|
|
|
|
/**
|
|
* Allows to read device identifiers and use ICC based authentication like EAP-AKA.
|
|
*
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
public static final String OPSTR_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER =
|
|
"android:use_icc_auth_with_device_identifier";
|
|
/**
|
|
* App output audio is being recorded
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final String OPSTR_RECORD_AUDIO_OUTPUT = "android:record_audio_output";
|
|
|
|
/**
|
|
* App can schedule exact alarm to perform timing based background work.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final String OPSTR_SCHEDULE_EXACT_ALARM = "android:schedule_exact_alarm";
|
|
|
|
/**
|
|
* Fine location being accessed by a location source, which is
|
|
* a component that already has location since it is the one that
|
|
* produces location.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final String OPSTR_FINE_LOCATION_SOURCE = "android:fine_location_source";
|
|
|
|
/**
|
|
* Coarse location being accessed by a location source, which is
|
|
* a component that already has location since it is the one that
|
|
* produces location.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final String OPSTR_COARSE_LOCATION_SOURCE = "android:coarse_location_source";
|
|
|
|
/**
|
|
* Camera is being recorded in sandboxed detection process.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final String OPSTR_CAMERA_SANDBOXED = "android:camera_sandboxed";
|
|
|
|
/**
|
|
* Audio is being recorded in sandboxed detection process.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final String OPSTR_RECORD_AUDIO_SANDBOXED = "android:record_audio_sandboxed";
|
|
|
|
/**
|
|
* Allow apps to create the requests to manage the media files without user confirmation.
|
|
*
|
|
* @see android.Manifest.permission#MANAGE_MEDIA
|
|
* @see android.provider.MediaStore#createDeleteRequest(ContentResolver, Collection)
|
|
* @see android.provider.MediaStore#createTrashRequest(ContentResolver, Collection, boolean)
|
|
* @see android.provider.MediaStore#createWriteRequest(ContentResolver, Collection)
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final String OPSTR_MANAGE_MEDIA = "android:manage_media";
|
|
/** @hide */
|
|
public static final String OPSTR_UWB_RANGING = "android:uwb_ranging";
|
|
/** @hide */
|
|
public static final String OPSTR_NEARBY_WIFI_DEVICES = "android:nearby_wifi_devices";
|
|
|
|
/**
|
|
* Activity recognition being accessed by an activity recognition source, which
|
|
* is a component that already has access since it is the one that detects
|
|
* activity recognition.
|
|
*
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
public static final String OPSTR_ACTIVITY_RECOGNITION_SOURCE =
|
|
"android:activity_recognition_source";
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
public static final String OPSTR_RECORD_INCOMING_PHONE_AUDIO =
|
|
"android:record_incoming_phone_audio";
|
|
|
|
/**
|
|
* VPN app establishes a connection through the VpnService API.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public static final String OPSTR_ESTABLISH_VPN_SERVICE = "android:establish_vpn_service";
|
|
|
|
/**
|
|
* VPN app establishes a connection through the VpnManager API.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public static final String OPSTR_ESTABLISH_VPN_MANAGER = "android:establish_vpn_manager";
|
|
|
|
/**
|
|
* Limit user accessing restricted settings.
|
|
*
|
|
* @hide
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
|
|
@SystemApi
|
|
public static final String OPSTR_ACCESS_RESTRICTED_SETTINGS =
|
|
"android:access_restricted_settings";
|
|
|
|
/**
|
|
* Receive microphone audio from an ambient sound detection event
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public static final String OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO =
|
|
"android:receive_ambient_trigger_audio";
|
|
/**
|
|
* Notify apps that they have been granted URI permission photos
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public static final String OPSTR_READ_MEDIA_VISUAL_USER_SELECTED =
|
|
"android:read_media_visual_user_selected";
|
|
|
|
/**
|
|
* An app op for reading/writing health connect data.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public static final String OPSTR_READ_WRITE_HEALTH_DATA =
|
|
"android:read_write_health_data";
|
|
|
|
/**
|
|
* Record audio from near-field microphone (ie. TV remote)
|
|
* Allows audio recording regardless of sensor privacy state,
|
|
* as it is an intentional user interaction: hold-to-talk
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
@SuppressLint("IntentName")
|
|
public static final String OPSTR_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO =
|
|
"android:receive_explicit_user_interaction_audio";
|
|
|
|
/**
|
|
* App can schedule user-initiated jobs.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final String OPSTR_RUN_USER_INITIATED_JOBS = "android:run_user_initiated_jobs";
|
|
|
|
/**
|
|
* Prevent an app from being suspended.
|
|
*
|
|
* Only to be used by the system.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final String OPSTR_SYSTEM_EXEMPT_FROM_SUSPENSION =
|
|
"android:system_exempt_from_suspension";
|
|
|
|
/**
|
|
* Allow an application to create non-dismissible notifications. Starting from Android U,
|
|
* notifications with the ongoing parameter can be dismissed by a user on an unlocked device
|
|
* unless the application that created the notification is exempt.
|
|
* An application with this appop will be made exempt.
|
|
*
|
|
* Only to be used by the system.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final String OPSTR_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS =
|
|
"android:system_exempt_from_dismissible_notifications";
|
|
|
|
/**
|
|
* Start a foreground service with the type "specialUse".
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final String OPSTR_FOREGROUND_SERVICE_SPECIAL_USE =
|
|
"android:foreground_service_special_use";
|
|
|
|
/**
|
|
* Exempt an app from all power-related restrictions, including app standby and doze.
|
|
* In addition, the app will be able to start foreground services from the background, and the
|
|
* user will not be able to stop foreground services run by the app.
|
|
*
|
|
* Only to be used by the system.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final String OPSTR_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS =
|
|
"android:system_exempt_from_power_restrictions";
|
|
|
|
/**
|
|
* Prevent an app from being placed into hibernation.
|
|
*
|
|
* Only to be used by the system.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public static final String OPSTR_SYSTEM_EXEMPT_FROM_HIBERNATION =
|
|
"android:system_exempt_from_hibernation";
|
|
|
|
/**
|
|
* Allows an application to start an activity while running in the background.
|
|
*
|
|
* Only to be used by the system.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final String OPSTR_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION =
|
|
"android:system_exempt_from_activity_bg_start_restriction";
|
|
|
|
/**
|
|
* Allows an application to capture bugreport directly without consent dialog when using the
|
|
* bugreporting API on userdebug/eng build.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public static final String OPSTR_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD =
|
|
"android:capture_consentless_bugreport_on_userdebug_build";
|
|
|
|
/**
|
|
* App op deprecated/removed.
|
|
* @hide
|
|
*/
|
|
public static final String OPSTR_DEPRECATED_2 = "android:deprecated_2";
|
|
|
|
/**
|
|
* Send an intent to launch instead of posting the notification to the status bar.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final String OPSTR_USE_FULL_SCREEN_INTENT = "android:use_full_screen_intent";
|
|
|
|
/**
|
|
* Allows the assistant app to be voice-triggered by detected hotwords from a trusted detection
|
|
* service.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final String OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO =
|
|
"android:receive_sandbox_trigger_audio";
|
|
|
|
/**
|
|
* App op has been deprecated.
|
|
* @hide
|
|
*/
|
|
public static final String OPSTR_DEPRECATED_3 = "android:deprecated_3";
|
|
|
|
/**
|
|
* Creation of an overlay using accessibility services
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
@FlaggedApi(FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED)
|
|
public static final String OPSTR_CREATE_ACCESSIBILITY_OVERLAY =
|
|
"android:create_accessibility_overlay";
|
|
|
|
/**
|
|
* Indicate that the user has enabled or disabled mobile data
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
@FlaggedApi(FLAG_OP_ENABLE_MOBILE_DATA_BY_USER)
|
|
public static final String OPSTR_ENABLE_MOBILE_DATA_BY_USER =
|
|
"android:enable_mobile_data_by_user";
|
|
|
|
/**
|
|
* Reserved for use by appop tests so that operations done legitimately by the platform don't
|
|
* interfere with expected results. Platform code should never use this.
|
|
*
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
@SuppressLint("UnflaggedApi")
|
|
public static final String OPSTR_RESERVED_FOR_TESTING =
|
|
"android:reserved_for_testing";
|
|
|
|
/**
|
|
* Rapid clearing of notifications by a notification listener
|
|
*
|
|
* @hide
|
|
*/
|
|
// See b/289080543 for more details
|
|
@SystemApi
|
|
@FlaggedApi(FLAG_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER_APP_OP_ENABLED)
|
|
public static final String OPSTR_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER =
|
|
"android:rapid_clear_notifications_by_listener";
|
|
|
|
/**
|
|
* Allows an application to read the system grammatical gender.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final String OPSTR_READ_SYSTEM_GRAMMATICAL_GENDER =
|
|
"android:read_system_grammatical_gender";
|
|
|
|
/**
|
|
* App op has been removed.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final String OPSTR_DEPRECATED_4 = "android:deprecated_4";
|
|
|
|
/**
|
|
* Allows an app to access location without the traditional location permissions and while the
|
|
* user location setting is off, but only during pre-defined emergency sessions.
|
|
*
|
|
* <p>This op is only used for tracking, not for permissions, so it is still the client's
|
|
* responsibility to check the {@link Manifest.permission.LOCATION_BYPASS} permission
|
|
* appropriately.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
@FlaggedApi(FLAG_LOCATION_BYPASS)
|
|
public static final String OPSTR_EMERGENCY_LOCATION = "android:emergency_location";
|
|
|
|
/** {@link #sAppOpsToNote} not initialized yet for this op */
|
|
private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
|
|
/** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
|
|
private static final byte SHOULD_NOT_COLLECT_NOTE_OP = 1;
|
|
/** Should collect noting of this app-op in {@link #sAppOpsToNote} */
|
|
private static final byte SHOULD_COLLECT_NOTE_OP = 2;
|
|
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@IntDef(flag = true, prefix = { "SHOULD_" }, value = {
|
|
SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED,
|
|
SHOULD_NOT_COLLECT_NOTE_OP,
|
|
SHOULD_COLLECT_NOTE_OP
|
|
})
|
|
private @interface ShouldCollectNoteOp {}
|
|
|
|
/** Whether noting for an appop should be collected */
|
|
private static final @ShouldCollectNoteOp byte[] sAppOpsToNote = new byte[_NUM_OP];
|
|
|
|
private static final int[] RUNTIME_PERMISSION_OPS = {
|
|
// Contacts
|
|
OP_READ_CONTACTS,
|
|
OP_WRITE_CONTACTS,
|
|
OP_GET_ACCOUNTS,
|
|
// Calendar
|
|
OP_READ_CALENDAR,
|
|
OP_WRITE_CALENDAR,
|
|
// SMS
|
|
OP_SEND_SMS,
|
|
OP_RECEIVE_SMS,
|
|
OP_READ_SMS,
|
|
OP_RECEIVE_WAP_PUSH,
|
|
OP_RECEIVE_MMS,
|
|
OP_READ_CELL_BROADCASTS,
|
|
// Storage
|
|
OP_READ_EXTERNAL_STORAGE,
|
|
OP_WRITE_EXTERNAL_STORAGE,
|
|
OP_ACCESS_MEDIA_LOCATION,
|
|
// Location
|
|
OP_COARSE_LOCATION,
|
|
OP_FINE_LOCATION,
|
|
// Phone
|
|
OP_READ_PHONE_STATE,
|
|
OP_READ_PHONE_NUMBERS,
|
|
OP_CALL_PHONE,
|
|
OP_READ_CALL_LOG,
|
|
OP_WRITE_CALL_LOG,
|
|
OP_ADD_VOICEMAIL,
|
|
OP_USE_SIP,
|
|
OP_PROCESS_OUTGOING_CALLS,
|
|
OP_ANSWER_PHONE_CALLS,
|
|
OP_ACCEPT_HANDOVER,
|
|
// Microphone
|
|
OP_RECORD_AUDIO,
|
|
// Camera
|
|
OP_CAMERA,
|
|
// Body sensors
|
|
OP_BODY_SENSORS,
|
|
// Activity recognition
|
|
OP_ACTIVITY_RECOGNITION,
|
|
// Aural
|
|
OP_READ_MEDIA_AUDIO,
|
|
// Visual
|
|
OP_READ_MEDIA_VIDEO,
|
|
OP_READ_MEDIA_IMAGES,
|
|
OP_READ_MEDIA_VISUAL_USER_SELECTED,
|
|
// Nearby devices
|
|
OP_BLUETOOTH_SCAN,
|
|
OP_BLUETOOTH_CONNECT,
|
|
OP_BLUETOOTH_ADVERTISE,
|
|
OP_UWB_RANGING,
|
|
OP_NEARBY_WIFI_DEVICES,
|
|
// Notifications
|
|
OP_POST_NOTIFICATION,
|
|
};
|
|
|
|
/**
|
|
* Ops for app op permissions that are setting the per-package mode for certain reasons. Most
|
|
* app op permissions should set the per-UID mode instead.
|
|
*/
|
|
private static final int[] APP_OP_PERMISSION_PACKAGE_OPS = {
|
|
OP_ACCESS_NOTIFICATIONS,
|
|
OP_SYSTEM_ALERT_WINDOW,
|
|
OP_WRITE_SETTINGS,
|
|
OP_GET_USAGE_STATS,
|
|
OP_REQUEST_INSTALL_PACKAGES,
|
|
OP_START_FOREGROUND,
|
|
OP_SMS_FINANCIAL_TRANSACTIONS,
|
|
OP_MANAGE_IPSEC_TUNNELS,
|
|
OP_INSTANT_APP_START_FOREGROUND,
|
|
OP_LOADER_USAGE_STATS
|
|
};
|
|
|
|
/**
|
|
* Ops for app op permissions that are setting the per-UID mode for certain reasons. This should
|
|
* be preferred over the per-package mode for new app op permissions.
|
|
*/
|
|
private static final int[] APP_OP_PERMISSION_UID_OPS = {
|
|
OP_MANAGE_EXTERNAL_STORAGE,
|
|
OP_INTERACT_ACROSS_PROFILES,
|
|
OP_MANAGE_ONGOING_CALLS,
|
|
OP_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER,
|
|
OP_SCHEDULE_EXACT_ALARM,
|
|
OP_MANAGE_MEDIA,
|
|
OP_TURN_SCREEN_ON,
|
|
OP_RUN_USER_INITIATED_JOBS,
|
|
OP_FOREGROUND_SERVICE_SPECIAL_USE,
|
|
OP_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD,
|
|
OP_USE_FULL_SCREEN_INTENT,
|
|
OP_RECEIVE_SANDBOX_TRIGGER_AUDIO,
|
|
OP_MEDIA_ROUTING_CONTROL,
|
|
OP_READ_SYSTEM_GRAMMATICAL_GENDER,
|
|
};
|
|
|
|
static final AppOpInfo[] sAppOpInfos = new AppOpInfo[]{
|
|
new AppOpInfo.Builder(OP_COARSE_LOCATION, OPSTR_COARSE_LOCATION, "COARSE_LOCATION")
|
|
.setPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
|
|
.setRestriction(UserManager.DISALLOW_SHARE_LOCATION)
|
|
.setAllowSystemRestrictionBypass(new RestrictionBypass(true, false, false))
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_FINE_LOCATION, OPSTR_FINE_LOCATION, "FINE_LOCATION")
|
|
.setPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
|
|
.setRestriction(UserManager.DISALLOW_SHARE_LOCATION)
|
|
.setAllowSystemRestrictionBypass(new RestrictionBypass(true, false, false))
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_GPS, OPSTR_GPS, "GPS")
|
|
.setSwitchCode(OP_COARSE_LOCATION)
|
|
.setRestriction(UserManager.DISALLOW_SHARE_LOCATION)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_VIBRATE, OPSTR_VIBRATE, "VIBRATE")
|
|
.setSwitchCode(OP_VIBRATE).setPermission(android.Manifest.permission.VIBRATE)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_READ_CONTACTS, OPSTR_READ_CONTACTS, "READ_CONTACTS")
|
|
.setPermission(android.Manifest.permission.READ_CONTACTS)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_WRITE_CONTACTS, OPSTR_WRITE_CONTACTS, "WRITE_CONTACTS")
|
|
.setPermission(android.Manifest.permission.WRITE_CONTACTS)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_READ_CALL_LOG, OPSTR_READ_CALL_LOG, "READ_CALL_LOG")
|
|
.setPermission(android.Manifest.permission.READ_CALL_LOG)
|
|
.setRestriction(UserManager.DISALLOW_OUTGOING_CALLS)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_WRITE_CALL_LOG, OPSTR_WRITE_CALL_LOG, "WRITE_CALL_LOG")
|
|
.setPermission(android.Manifest.permission.WRITE_CALL_LOG)
|
|
.setRestriction(UserManager.DISALLOW_OUTGOING_CALLS)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_READ_CALENDAR, OPSTR_READ_CALENDAR, "READ_CALENDAR")
|
|
.setPermission(android.Manifest.permission.READ_CALENDAR)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_WRITE_CALENDAR, OPSTR_WRITE_CALENDAR, "WRITE_CALENDAR")
|
|
.setPermission(android.Manifest.permission.WRITE_CALENDAR)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_WIFI_SCAN, OPSTR_WIFI_SCAN, "WIFI_SCAN")
|
|
.setSwitchCode(OP_COARSE_LOCATION)
|
|
.setPermission(android.Manifest.permission.ACCESS_WIFI_STATE)
|
|
.setRestriction(UserManager.DISALLOW_SHARE_LOCATION)
|
|
.setAllowSystemRestrictionBypass(new RestrictionBypass(false, true, false))
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_POST_NOTIFICATION, OPSTR_POST_NOTIFICATION, "POST_NOTIFICATION")
|
|
.setPermission(android.Manifest.permission.POST_NOTIFICATIONS)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_NEIGHBORING_CELLS, OPSTR_NEIGHBORING_CELLS, "NEIGHBORING_CELLS")
|
|
.setSwitchCode(OP_COARSE_LOCATION).setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_CALL_PHONE, OPSTR_CALL_PHONE, "CALL_PHONE")
|
|
.setSwitchCode(OP_CALL_PHONE).setPermission(android.Manifest.permission.CALL_PHONE)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_READ_SMS, OPSTR_READ_SMS, "READ_SMS")
|
|
.setPermission(android.Manifest.permission.READ_SMS)
|
|
.setRestriction(UserManager.DISALLOW_SMS).setDefaultMode(AppOpsManager.MODE_ALLOWED)
|
|
.setDisableReset(true).build(),
|
|
new AppOpInfo.Builder(OP_WRITE_SMS, OPSTR_WRITE_SMS, "WRITE_SMS")
|
|
.setRestriction(UserManager.DISALLOW_SMS)
|
|
.setDefaultMode(AppOpsManager.MODE_IGNORED).setDisableReset(true).build(),
|
|
new AppOpInfo.Builder(OP_RECEIVE_SMS, OPSTR_RECEIVE_SMS, "RECEIVE_SMS")
|
|
.setPermission(android.Manifest.permission.RECEIVE_SMS)
|
|
.setRestriction(UserManager.DISALLOW_SMS)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).setDisableReset(true).build(),
|
|
new AppOpInfo.Builder(OP_RECEIVE_EMERGECY_SMS, OPSTR_RECEIVE_EMERGENCY_BROADCAST,
|
|
"RECEIVE_EMERGENCY_BROADCAST").setSwitchCode(OP_RECEIVE_SMS)
|
|
.setPermission(android.Manifest.permission.RECEIVE_EMERGENCY_BROADCAST)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_RECEIVE_MMS, OPSTR_RECEIVE_MMS, "RECEIVE_MMS")
|
|
.setPermission(android.Manifest.permission.RECEIVE_MMS)
|
|
.setRestriction(UserManager.DISALLOW_SMS).setDefaultMode(AppOpsManager.MODE_ALLOWED)
|
|
.build(),
|
|
new AppOpInfo.Builder(OP_RECEIVE_WAP_PUSH, OPSTR_RECEIVE_WAP_PUSH, "RECEIVE_WAP_PUSH")
|
|
.setPermission(android.Manifest.permission.RECEIVE_WAP_PUSH)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).setDisableReset(true).build(),
|
|
new AppOpInfo.Builder(OP_SEND_SMS, OPSTR_SEND_SMS, "SEND_SMS")
|
|
.setPermission(android.Manifest.permission.SEND_SMS)
|
|
.setRestriction(UserManager.DISALLOW_SMS).setDefaultMode(AppOpsManager.MODE_ALLOWED)
|
|
.setDisableReset(true).build(),
|
|
new AppOpInfo.Builder(OP_READ_ICC_SMS, OPSTR_READ_ICC_SMS, "READ_ICC_SMS")
|
|
.setSwitchCode(OP_READ_SMS).setPermission(android.Manifest.permission.READ_SMS)
|
|
.setRestriction(UserManager.DISALLOW_SMS).setDefaultMode(AppOpsManager.MODE_ALLOWED)
|
|
.build(),
|
|
new AppOpInfo.Builder(OP_WRITE_ICC_SMS, OPSTR_WRITE_ICC_SMS, "WRITE_ICC_SMS")
|
|
.setSwitchCode(OP_WRITE_SMS).setRestriction(UserManager.DISALLOW_SMS)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_WRITE_SETTINGS, OPSTR_WRITE_SETTINGS, "WRITE_SETTINGS")
|
|
.setPermission(android.Manifest.permission.WRITE_SETTINGS).build(),
|
|
new AppOpInfo.Builder(OP_SYSTEM_ALERT_WINDOW, OPSTR_SYSTEM_ALERT_WINDOW,
|
|
"SYSTEM_ALERT_WINDOW")
|
|
.setPermission(android.Manifest.permission.SYSTEM_ALERT_WINDOW)
|
|
.setRestriction(UserManager.DISALLOW_CREATE_WINDOWS)
|
|
.setAllowSystemRestrictionBypass(new RestrictionBypass(false, true, false))
|
|
.setDefaultMode(getSystemAlertWindowDefault()).build(),
|
|
new AppOpInfo.Builder(OP_ACCESS_NOTIFICATIONS, OPSTR_ACCESS_NOTIFICATIONS,
|
|
"ACCESS_NOTIFICATIONS")
|
|
.setPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS).build(),
|
|
new AppOpInfo.Builder(OP_CAMERA, OPSTR_CAMERA, "CAMERA")
|
|
.setPermission(android.Manifest.permission.CAMERA)
|
|
.setRestriction(UserManager.DISALLOW_CAMERA)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_RECORD_AUDIO, OPSTR_RECORD_AUDIO, "RECORD_AUDIO")
|
|
.setPermission(android.Manifest.permission.RECORD_AUDIO)
|
|
.setRestriction(UserManager.DISALLOW_RECORD_AUDIO)
|
|
.setAllowSystemRestrictionBypass(new RestrictionBypass(false, false, true))
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_PLAY_AUDIO, OPSTR_PLAY_AUDIO, "PLAY_AUDIO")
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_READ_CLIPBOARD, OPSTR_READ_CLIPBOARD, "READ_CLIPBOARD")
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_WRITE_CLIPBOARD, OPSTR_WRITE_CLIPBOARD, "WRITE_CLIPBOARD")
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_TAKE_MEDIA_BUTTONS, OPSTR_TAKE_MEDIA_BUTTONS, "TAKE_MEDIA_BUTTONS")
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED)
|
|
.build(),
|
|
new AppOpInfo.Builder(OP_TAKE_AUDIO_FOCUS, OPSTR_TAKE_AUDIO_FOCUS, "TAKE_AUDIO_FOCUS")
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_AUDIO_MASTER_VOLUME, OPSTR_AUDIO_MASTER_VOLUME,
|
|
"AUDIO_MASTER_VOLUME").setSwitchCode(OP_AUDIO_MASTER_VOLUME)
|
|
.setRestriction(UserManager.DISALLOW_ADJUST_VOLUME)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_AUDIO_VOICE_VOLUME, OPSTR_AUDIO_VOICE_VOLUME, "AUDIO_VOICE_VOLUME")
|
|
.setRestriction(UserManager.DISALLOW_ADJUST_VOLUME)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_AUDIO_RING_VOLUME, OPSTR_AUDIO_RING_VOLUME, "AUDIO_RING_VOLUME")
|
|
.setRestriction(UserManager.DISALLOW_ADJUST_VOLUME)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_AUDIO_MEDIA_VOLUME, OPSTR_AUDIO_MEDIA_VOLUME, "AUDIO_MEDIA_VOLUME")
|
|
.setRestriction(UserManager.DISALLOW_ADJUST_VOLUME)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_AUDIO_ALARM_VOLUME, OPSTR_AUDIO_ALARM_VOLUME, "AUDIO_ALARM_VOLUME")
|
|
.setRestriction(UserManager.DISALLOW_ADJUST_VOLUME)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_AUDIO_NOTIFICATION_VOLUME, OPSTR_AUDIO_NOTIFICATION_VOLUME,
|
|
"AUDIO_NOTIFICATION_VOLUME").setSwitchCode(OP_AUDIO_NOTIFICATION_VOLUME)
|
|
.setRestriction(UserManager.DISALLOW_ADJUST_VOLUME)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_AUDIO_BLUETOOTH_VOLUME, OPSTR_AUDIO_BLUETOOTH_VOLUME,
|
|
"AUDIO_BLUETOOTH_VOLUME").setRestriction(UserManager.DISALLOW_ADJUST_VOLUME)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_WAKE_LOCK, OPSTR_WAKE_LOCK, "WAKE_LOCK")
|
|
.setPermission(android.Manifest.permission.WAKE_LOCK)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_MONITOR_LOCATION, OPSTR_MONITOR_LOCATION, "MONITOR_LOCATION")
|
|
.setSwitchCode(OP_COARSE_LOCATION)
|
|
.setRestriction(UserManager.DISALLOW_SHARE_LOCATION)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_MONITOR_HIGH_POWER_LOCATION, OPSTR_MONITOR_HIGH_POWER_LOCATION,
|
|
"MONITOR_HIGH_POWER_LOCATION").setSwitchCode(OP_COARSE_LOCATION)
|
|
.setRestriction(UserManager.DISALLOW_SHARE_LOCATION)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_GET_USAGE_STATS, OPSTR_GET_USAGE_STATS, "GET_USAGE_STATS")
|
|
.setPermission(android.Manifest.permission.PACKAGE_USAGE_STATS).build(),
|
|
new AppOpInfo.Builder(OP_MUTE_MICROPHONE, OPSTR_MUTE_MICROPHONE, "MUTE_MICROPHONE")
|
|
.setRestriction(UserManager.DISALLOW_UNMUTE_MICROPHONE)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_TOAST_WINDOW, OPSTR_TOAST_WINDOW, "TOAST_WINDOW")
|
|
.setRestriction(UserManager.DISALLOW_CREATE_WINDOWS)
|
|
.setAllowSystemRestrictionBypass(new RestrictionBypass(false, true, false))
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_PROJECT_MEDIA, OPSTR_PROJECT_MEDIA, "PROJECT_MEDIA")
|
|
.setDefaultMode(AppOpsManager.MODE_IGNORED).build(),
|
|
new AppOpInfo.Builder(OP_ACTIVATE_VPN, OPSTR_ACTIVATE_VPN, "ACTIVATE_VPN")
|
|
.setDefaultMode(AppOpsManager.MODE_IGNORED).build(),
|
|
new AppOpInfo.Builder(OP_WRITE_WALLPAPER, OPSTR_WRITE_WALLPAPER, "WRITE_WALLPAPER")
|
|
.setRestriction(UserManager.DISALLOW_WALLPAPER)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_ASSIST_STRUCTURE, OPSTR_ASSIST_STRUCTURE, "ASSIST_STRUCTURE")
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_ASSIST_SCREENSHOT, OPSTR_ASSIST_SCREENSHOT, "ASSIST_SCREENSHOT")
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED)
|
|
.build(),
|
|
new AppOpInfo.Builder(OP_READ_PHONE_STATE, OPSTR_READ_PHONE_STATE, "READ_PHONE_STATE")
|
|
.setPermission(Manifest.permission.READ_PHONE_STATE)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_ADD_VOICEMAIL, OPSTR_ADD_VOICEMAIL, "ADD_VOICEMAIL")
|
|
.setPermission(Manifest.permission.ADD_VOICEMAIL)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_USE_SIP, OPSTR_USE_SIP, "USE_SIP")
|
|
.setPermission(Manifest.permission.USE_SIP)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_PROCESS_OUTGOING_CALLS, OPSTR_PROCESS_OUTGOING_CALLS,
|
|
"PROCESS_OUTGOING_CALLS").setSwitchCode(OP_PROCESS_OUTGOING_CALLS)
|
|
.setPermission(Manifest.permission.PROCESS_OUTGOING_CALLS)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_USE_FINGERPRINT, OPSTR_USE_FINGERPRINT, "USE_FINGERPRINT")
|
|
.setPermission(Manifest.permission.USE_FINGERPRINT)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_BODY_SENSORS, OPSTR_BODY_SENSORS, "BODY_SENSORS")
|
|
.setPermission(Manifest.permission.BODY_SENSORS)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_READ_CELL_BROADCASTS, OPSTR_READ_CELL_BROADCASTS,
|
|
"READ_CELL_BROADCASTS").setPermission(Manifest.permission.READ_CELL_BROADCASTS)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).setDisableReset(true).build(),
|
|
new AppOpInfo.Builder(OP_MOCK_LOCATION, OPSTR_MOCK_LOCATION, "MOCK_LOCATION")
|
|
.setDefaultMode(AppOpsManager.MODE_ERRORED).build(),
|
|
new AppOpInfo.Builder(OP_READ_EXTERNAL_STORAGE, OPSTR_READ_EXTERNAL_STORAGE,
|
|
"READ_EXTERNAL_STORAGE").setPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_WRITE_EXTERNAL_STORAGE, OPSTR_WRITE_EXTERNAL_STORAGE,
|
|
"WRITE_EXTERNAL_STORAGE").setPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_TURN_SCREEN_ON, OPSTR_TURN_SCREEN_ON, "TURN_SCREEN_ON")
|
|
.setPermission(Manifest.permission.TURN_SCREEN_ON)
|
|
.setDefaultMode(AppOpsManager.MODE_DEFAULT).build(),
|
|
new AppOpInfo.Builder(OP_GET_ACCOUNTS, OPSTR_GET_ACCOUNTS, "GET_ACCOUNTS")
|
|
.setPermission(Manifest.permission.GET_ACCOUNTS)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_RUN_IN_BACKGROUND, OPSTR_RUN_IN_BACKGROUND, "RUN_IN_BACKGROUND")
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED)
|
|
.build(),
|
|
new AppOpInfo.Builder(OP_AUDIO_ACCESSIBILITY_VOLUME, OPSTR_AUDIO_ACCESSIBILITY_VOLUME,
|
|
"AUDIO_ACCESSIBILITY_VOLUME")
|
|
.setRestriction(UserManager.DISALLOW_ADJUST_VOLUME)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_READ_PHONE_NUMBERS, OPSTR_READ_PHONE_NUMBERS, "READ_PHONE_NUMBERS")
|
|
.setPermission(Manifest.permission.READ_PHONE_NUMBERS)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_REQUEST_INSTALL_PACKAGES, OPSTR_REQUEST_INSTALL_PACKAGES,
|
|
"REQUEST_INSTALL_PACKAGES").setSwitchCode(OP_REQUEST_INSTALL_PACKAGES)
|
|
.setPermission(Manifest.permission.REQUEST_INSTALL_PACKAGES).build(),
|
|
new AppOpInfo.Builder(OP_PICTURE_IN_PICTURE, OPSTR_PICTURE_IN_PICTURE, "PICTURE_IN_PICTURE")
|
|
.setSwitchCode(OP_PICTURE_IN_PICTURE).setDefaultMode(AppOpsManager.MODE_ALLOWED)
|
|
.build(),
|
|
new AppOpInfo.Builder(OP_INSTANT_APP_START_FOREGROUND, OPSTR_INSTANT_APP_START_FOREGROUND,
|
|
"INSTANT_APP_START_FOREGROUND")
|
|
.setPermission(Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE).build(),
|
|
new AppOpInfo.Builder(OP_ANSWER_PHONE_CALLS, OPSTR_ANSWER_PHONE_CALLS, "ANSWER_PHONE_CALLS")
|
|
.setSwitchCode(OP_ANSWER_PHONE_CALLS)
|
|
.setPermission(Manifest.permission.ANSWER_PHONE_CALLS)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_RUN_ANY_IN_BACKGROUND, OPSTR_RUN_ANY_IN_BACKGROUND,
|
|
"RUN_ANY_IN_BACKGROUND")
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_CHANGE_WIFI_STATE, OPSTR_CHANGE_WIFI_STATE, "CHANGE_WIFI_STATE")
|
|
.setSwitchCode(OP_CHANGE_WIFI_STATE)
|
|
.setPermission(Manifest.permission.CHANGE_WIFI_STATE)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_REQUEST_DELETE_PACKAGES, OPSTR_REQUEST_DELETE_PACKAGES,
|
|
"REQUEST_DELETE_PACKAGES")
|
|
.setPermission(Manifest.permission.REQUEST_DELETE_PACKAGES)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_BIND_ACCESSIBILITY_SERVICE, OPSTR_BIND_ACCESSIBILITY_SERVICE,
|
|
"BIND_ACCESSIBILITY_SERVICE")
|
|
.setPermission(Manifest.permission.BIND_ACCESSIBILITY_SERVICE)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_ACCEPT_HANDOVER, OPSTR_ACCEPT_HANDOVER, "ACCEPT_HANDOVER")
|
|
.setSwitchCode(OP_ACCEPT_HANDOVER)
|
|
.setPermission(Manifest.permission.ACCEPT_HANDOVER)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_MANAGE_IPSEC_TUNNELS, OPSTR_MANAGE_IPSEC_TUNNELS,
|
|
"MANAGE_IPSEC_TUNNELS")
|
|
.setPermission(Manifest.permission.MANAGE_IPSEC_TUNNELS)
|
|
.setDefaultMode(AppOpsManager.MODE_ERRORED).build(),
|
|
new AppOpInfo.Builder(OP_START_FOREGROUND, OPSTR_START_FOREGROUND, "START_FOREGROUND")
|
|
.setPermission(Manifest.permission.FOREGROUND_SERVICE)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_BLUETOOTH_SCAN, OPSTR_BLUETOOTH_SCAN, "BLUETOOTH_SCAN")
|
|
.setPermission(Manifest.permission.BLUETOOTH_SCAN)
|
|
.setAllowSystemRestrictionBypass(new RestrictionBypass(false, true, false))
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_USE_BIOMETRIC, OPSTR_USE_BIOMETRIC, "USE_BIOMETRIC")
|
|
.setPermission(Manifest.permission.USE_BIOMETRIC)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_ACTIVITY_RECOGNITION, OPSTR_ACTIVITY_RECOGNITION,
|
|
"ACTIVITY_RECOGNITION")
|
|
.setPermission(Manifest.permission.ACTIVITY_RECOGNITION)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_SMS_FINANCIAL_TRANSACTIONS, OPSTR_SMS_FINANCIAL_TRANSACTIONS,
|
|
"SMS_FINANCIAL_TRANSACTIONS")
|
|
.setPermission(Manifest.permission.SMS_FINANCIAL_TRANSACTIONS)
|
|
.setRestriction(UserManager.DISALLOW_SMS).build(),
|
|
new AppOpInfo.Builder(OP_READ_MEDIA_AUDIO, OPSTR_READ_MEDIA_AUDIO, "READ_MEDIA_AUDIO")
|
|
.setPermission(Manifest.permission.READ_MEDIA_AUDIO)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_WRITE_MEDIA_AUDIO, OPSTR_WRITE_MEDIA_AUDIO, "WRITE_MEDIA_AUDIO")
|
|
.setDefaultMode(AppOpsManager.MODE_ERRORED).build(),
|
|
new AppOpInfo.Builder(OP_READ_MEDIA_VIDEO, OPSTR_READ_MEDIA_VIDEO, "READ_MEDIA_VIDEO")
|
|
.setPermission(Manifest.permission.READ_MEDIA_VIDEO)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_WRITE_MEDIA_VIDEO, OPSTR_WRITE_MEDIA_VIDEO, "WRITE_MEDIA_VIDEO")
|
|
.setDefaultMode(AppOpsManager.MODE_ERRORED).setDisableReset(true).build(),
|
|
new AppOpInfo.Builder(OP_READ_MEDIA_IMAGES, OPSTR_READ_MEDIA_IMAGES, "READ_MEDIA_IMAGES")
|
|
.setPermission(Manifest.permission.READ_MEDIA_IMAGES)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_WRITE_MEDIA_IMAGES, OPSTR_WRITE_MEDIA_IMAGES, "WRITE_MEDIA_IMAGES")
|
|
.setDefaultMode(AppOpsManager.MODE_ERRORED).setDisableReset(true).build(),
|
|
new AppOpInfo.Builder(OP_LEGACY_STORAGE, OPSTR_LEGACY_STORAGE, "LEGACY_STORAGE")
|
|
.setDisableReset(true).build(),
|
|
new AppOpInfo.Builder(OP_ACCESS_ACCESSIBILITY, OPSTR_ACCESS_ACCESSIBILITY,
|
|
"ACCESS_ACCESSIBILITY").setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_READ_DEVICE_IDENTIFIERS, OPSTR_READ_DEVICE_IDENTIFIERS,
|
|
"READ_DEVICE_IDENTIFIERS").setDefaultMode(AppOpsManager.MODE_ERRORED).build(),
|
|
new AppOpInfo.Builder(OP_ACCESS_MEDIA_LOCATION, OPSTR_ACCESS_MEDIA_LOCATION,
|
|
"ACCESS_MEDIA_LOCATION").setPermission(Manifest.permission.ACCESS_MEDIA_LOCATION)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_QUERY_ALL_PACKAGES, OPSTR_QUERY_ALL_PACKAGES, "QUERY_ALL_PACKAGES")
|
|
.build(),
|
|
new AppOpInfo.Builder(OP_MANAGE_EXTERNAL_STORAGE, OPSTR_MANAGE_EXTERNAL_STORAGE,
|
|
"MANAGE_EXTERNAL_STORAGE")
|
|
.setPermission(Manifest.permission.MANAGE_EXTERNAL_STORAGE).build(),
|
|
new AppOpInfo.Builder(OP_INTERACT_ACROSS_PROFILES, OPSTR_INTERACT_ACROSS_PROFILES,
|
|
"INTERACT_ACROSS_PROFILES")
|
|
.setPermission(android.Manifest.permission.INTERACT_ACROSS_PROFILES).build(),
|
|
new AppOpInfo.Builder(OP_ACTIVATE_PLATFORM_VPN, OPSTR_ACTIVATE_PLATFORM_VPN,
|
|
"ACTIVATE_PLATFORM_VPN").setDefaultMode(AppOpsManager.MODE_IGNORED).build(),
|
|
new AppOpInfo.Builder(OP_LOADER_USAGE_STATS, OPSTR_LOADER_USAGE_STATS, "LOADER_USAGE_STATS")
|
|
.setPermission(android.Manifest.permission.LOADER_USAGE_STATS).build(),
|
|
new AppOpInfo.Builder(OP_NONE, "", "").setDefaultMode(AppOpsManager.MODE_IGNORED).build(),
|
|
new AppOpInfo.Builder(OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
|
|
OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, "AUTO_REVOKE_PERMISSIONS_IF_UNUSED")
|
|
.build(),
|
|
new AppOpInfo.Builder(OP_AUTO_REVOKE_MANAGED_BY_INSTALLER,
|
|
OPSTR_AUTO_REVOKE_MANAGED_BY_INSTALLER, "AUTO_REVOKE_MANAGED_BY_INSTALLER")
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_NO_ISOLATED_STORAGE, OPSTR_NO_ISOLATED_STORAGE,
|
|
"NO_ISOLATED_STORAGE").setDefaultMode(AppOpsManager.MODE_ERRORED)
|
|
.setDisableReset(true).build(),
|
|
new AppOpInfo.Builder(OP_PHONE_CALL_MICROPHONE, OPSTR_PHONE_CALL_MICROPHONE,
|
|
"PHONE_CALL_MICROPHONE").setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_PHONE_CALL_CAMERA, OPSTR_PHONE_CALL_CAMERA, "PHONE_CALL_CAMERA")
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_RECORD_AUDIO_HOTWORD, OPSTR_RECORD_AUDIO_HOTWORD,
|
|
"RECORD_AUDIO_HOTWORD").setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_MANAGE_ONGOING_CALLS, OPSTR_MANAGE_ONGOING_CALLS,
|
|
"MANAGE_ONGOING_CALLS").setPermission(Manifest.permission.MANAGE_ONGOING_CALLS)
|
|
.setDisableReset(true).build(),
|
|
new AppOpInfo.Builder(OP_MANAGE_CREDENTIALS, OPSTR_MANAGE_CREDENTIALS, "MANAGE_CREDENTIALS")
|
|
.build(),
|
|
new AppOpInfo.Builder(OP_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER,
|
|
OPSTR_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER, "USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER")
|
|
.setPermission(Manifest.permission.USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER)
|
|
.setDisableReset(true).build(),
|
|
new AppOpInfo.Builder(OP_RECORD_AUDIO_OUTPUT, OPSTR_RECORD_AUDIO_OUTPUT,
|
|
"RECORD_AUDIO_OUTPUT").setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_SCHEDULE_EXACT_ALARM, OPSTR_SCHEDULE_EXACT_ALARM,
|
|
"SCHEDULE_EXACT_ALARM").setPermission(Manifest.permission.SCHEDULE_EXACT_ALARM)
|
|
.build(),
|
|
new AppOpInfo.Builder(OP_FINE_LOCATION_SOURCE, OPSTR_FINE_LOCATION_SOURCE,
|
|
"FINE_LOCATION_SOURCE").setSwitchCode(OP_FINE_LOCATION)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_COARSE_LOCATION_SOURCE, OPSTR_COARSE_LOCATION_SOURCE,
|
|
"COARSE_LOCATION_SOURCE").setSwitchCode(OP_COARSE_LOCATION)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_MANAGE_MEDIA, OPSTR_MANAGE_MEDIA, "MANAGE_MEDIA")
|
|
.setPermission(Manifest.permission.MANAGE_MEDIA).build(),
|
|
new AppOpInfo.Builder(OP_BLUETOOTH_CONNECT, OPSTR_BLUETOOTH_CONNECT, "BLUETOOTH_CONNECT")
|
|
.setPermission(Manifest.permission.BLUETOOTH_CONNECT)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_UWB_RANGING, OPSTR_UWB_RANGING, "UWB_RANGING")
|
|
.setPermission(Manifest.permission.UWB_RANGING)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_ACTIVITY_RECOGNITION_SOURCE, OPSTR_ACTIVITY_RECOGNITION_SOURCE,
|
|
"ACTIVITY_RECOGNITION_SOURCE")
|
|
.setSwitchCode(OP_ACTIVITY_RECOGNITION).setDefaultMode(AppOpsManager.MODE_ALLOWED)
|
|
.build(),
|
|
new AppOpInfo.Builder(OP_BLUETOOTH_ADVERTISE, OPSTR_BLUETOOTH_ADVERTISE,
|
|
"BLUETOOTH_ADVERTISE").setPermission(Manifest.permission.BLUETOOTH_ADVERTISE)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_RECORD_INCOMING_PHONE_AUDIO, OPSTR_RECORD_INCOMING_PHONE_AUDIO,
|
|
"RECORD_INCOMING_PHONE_AUDIO").setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_NEARBY_WIFI_DEVICES, OPSTR_NEARBY_WIFI_DEVICES,
|
|
"NEARBY_WIFI_DEVICES").setPermission(Manifest.permission.NEARBY_WIFI_DEVICES)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_ESTABLISH_VPN_SERVICE, OPSTR_ESTABLISH_VPN_SERVICE,
|
|
"ESTABLISH_VPN_SERVICE").setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_ESTABLISH_VPN_MANAGER, OPSTR_ESTABLISH_VPN_MANAGER,
|
|
"ESTABLISH_VPN_MANAGER").setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_ACCESS_RESTRICTED_SETTINGS, OPSTR_ACCESS_RESTRICTED_SETTINGS,
|
|
"ACCESS_RESTRICTED_SETTINGS").setDefaultMode(
|
|
android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
|
|
? MODE_DEFAULT : MODE_ALLOWED)
|
|
.setDisableReset(true).setRestrictRead(true).build(),
|
|
new AppOpInfo.Builder(OP_RECEIVE_AMBIENT_TRIGGER_AUDIO, OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO,
|
|
"RECEIVE_SOUNDTRIGGER_AUDIO").setDefaultMode(AppOpsManager.MODE_ALLOWED)
|
|
.setForceCollectNotes(true).build(),
|
|
new AppOpInfo.Builder(OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO,
|
|
OPSTR_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO,
|
|
"RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO").setDefaultMode(
|
|
AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_RUN_USER_INITIATED_JOBS, OPSTR_RUN_USER_INITIATED_JOBS,
|
|
"RUN_USER_INITIATED_JOBS").setDefaultMode(AppOpsManager.MODE_ALLOWED)
|
|
.build(),
|
|
new AppOpInfo.Builder(OP_READ_MEDIA_VISUAL_USER_SELECTED,
|
|
OPSTR_READ_MEDIA_VISUAL_USER_SELECTED, "READ_MEDIA_VISUAL_USER_SELECTED")
|
|
.setPermission(Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED)
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_SYSTEM_EXEMPT_FROM_SUSPENSION,
|
|
OPSTR_SYSTEM_EXEMPT_FROM_SUSPENSION,
|
|
"SYSTEM_EXEMPT_FROM_SUSPENSION")
|
|
.setDisableReset(true).build(),
|
|
new AppOpInfo.Builder(OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS,
|
|
OPSTR_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS,
|
|
"SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS")
|
|
.setDisableReset(true).build(),
|
|
new AppOpInfo.Builder(OP_READ_WRITE_HEALTH_DATA, OPSTR_READ_WRITE_HEALTH_DATA,
|
|
"READ_WRITE_HEALTH_DATA").setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_FOREGROUND_SERVICE_SPECIAL_USE,
|
|
OPSTR_FOREGROUND_SERVICE_SPECIAL_USE, "FOREGROUND_SERVICE_SPECIAL_USE")
|
|
.setPermission(Manifest.permission.FOREGROUND_SERVICE_SPECIAL_USE).build(),
|
|
new AppOpInfo.Builder(OP_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS,
|
|
OPSTR_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS,
|
|
"SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS")
|
|
.setDisableReset(true).build(),
|
|
new AppOpInfo.Builder(OP_SYSTEM_EXEMPT_FROM_HIBERNATION,
|
|
OPSTR_SYSTEM_EXEMPT_FROM_HIBERNATION,
|
|
"SYSTEM_EXEMPT_FROM_HIBERNATION")
|
|
.setDisableReset(true).build(),
|
|
new AppOpInfo.Builder(OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION,
|
|
OPSTR_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION,
|
|
"SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION")
|
|
.setDisableReset(true).build(),
|
|
new AppOpInfo.Builder(
|
|
OP_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD,
|
|
OPSTR_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD,
|
|
"CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD")
|
|
.setPermission(Manifest.permission.CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD)
|
|
.build(),
|
|
new AppOpInfo.Builder(OP_DEPRECATED_2, OPSTR_DEPRECATED_2, "DEPRECATED_2")
|
|
.setDefaultMode(AppOpsManager.MODE_IGNORED).build(),
|
|
new AppOpInfo.Builder(OP_USE_FULL_SCREEN_INTENT, OPSTR_USE_FULL_SCREEN_INTENT,
|
|
"USE_FULL_SCREEN_INTENT").setPermission(Manifest.permission.USE_FULL_SCREEN_INTENT)
|
|
.build(),
|
|
new AppOpInfo.Builder(OP_CAMERA_SANDBOXED, OPSTR_CAMERA_SANDBOXED,
|
|
"CAMERA_SANDBOXED").setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_RECORD_AUDIO_SANDBOXED, OPSTR_RECORD_AUDIO_SANDBOXED,
|
|
"RECORD_AUDIO_SANDBOXED").setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_RECEIVE_SANDBOX_TRIGGER_AUDIO,
|
|
OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO,
|
|
"RECEIVE_SANDBOX_TRIGGER_AUDIO")
|
|
.setPermission(Manifest.permission.RECEIVE_SANDBOX_TRIGGER_AUDIO)
|
|
.setDefaultMode(AppOpsManager.MODE_DEFAULT).build(),
|
|
new AppOpInfo.Builder(OP_DEPRECATED_3, OPSTR_DEPRECATED_3, "DEPRECATED_3")
|
|
.setDefaultMode(AppOpsManager.MODE_IGNORED).build(),
|
|
new AppOpInfo.Builder(OP_CREATE_ACCESSIBILITY_OVERLAY,
|
|
OPSTR_CREATE_ACCESSIBILITY_OVERLAY,
|
|
"CREATE_ACCESSIBILITY_OVERLAY")
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_MEDIA_ROUTING_CONTROL, OPSTR_MEDIA_ROUTING_CONTROL,
|
|
"MEDIA_ROUTING_CONTROL")
|
|
.setPermission(Manifest.permission.MEDIA_ROUTING_CONTROL).build(),
|
|
new AppOpInfo.Builder(OP_ENABLE_MOBILE_DATA_BY_USER, OPSTR_ENABLE_MOBILE_DATA_BY_USER,
|
|
"ENABLE_MOBILE_DATA_BY_USER").setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_RESERVED_FOR_TESTING, OPSTR_RESERVED_FOR_TESTING,
|
|
"OP_RESERVED_FOR_TESTING").setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER,
|
|
OPSTR_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER,
|
|
"RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER")
|
|
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_READ_SYSTEM_GRAMMATICAL_GENDER,
|
|
OPSTR_READ_SYSTEM_GRAMMATICAL_GENDER, "READ_SYSTEM_GRAMMATICAL_GENDER")
|
|
// will make it an app-op permission in the future.
|
|
// .setPermission(Manifest.permission.READ_SYSTEM_GRAMMATICAL_GENDER)
|
|
.build(),
|
|
new AppOpInfo.Builder(OP_DEPRECATED_4, OPSTR_DEPRECATED_4, "DEPRECATED_4")
|
|
.setDefaultMode(AppOpsManager.MODE_IGNORED).build(),
|
|
new AppOpInfo.Builder(OP_ARCHIVE_ICON_OVERLAY, OPSTR_ARCHIVE_ICON_OVERLAY,
|
|
"ARCHIVE_ICON_OVERLAY")
|
|
.setDefaultMode(MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_UNARCHIVAL_CONFIRMATION, OPSTR_UNARCHIVAL_CONFIRMATION,
|
|
"UNARCHIVAL_CONFIRMATION")
|
|
.setDefaultMode(MODE_ALLOWED).build(),
|
|
new AppOpInfo.Builder(OP_EMERGENCY_LOCATION, OPSTR_EMERGENCY_LOCATION, "EMERGENCY_LOCATION")
|
|
.setDefaultMode(MODE_ALLOWED)
|
|
// even though this has a permission associated, this op is only used for tracking,
|
|
// and the client is responsible for checking the LOCATION_BYPASS permission.
|
|
.setPermission(Manifest.permission.LOCATION_BYPASS).build(),
|
|
};
|
|
|
|
// The number of longs needed to form a full bitmask of app ops
|
|
private static final int BITMASK_LEN = ((_NUM_OP - 1) / Long.SIZE) + 1;
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
public static boolean shouldForceCollectNoteForOp(int op) {
|
|
Preconditions.checkArgumentInRange(op, 0, _NUM_OP - 1, "opCode");
|
|
return sAppOpInfos[op].forceCollectNotes;
|
|
}
|
|
|
|
/**
|
|
* Mapping from an app op name to the app op code.
|
|
*/
|
|
private static HashMap<String, Integer> sOpStrToOp = new HashMap<>();
|
|
|
|
/**
|
|
* Mapping from a permission to the corresponding app op.
|
|
*/
|
|
private static HashMap<String, Integer> sPermToOp = new HashMap<>();
|
|
|
|
/**
|
|
* Set to the uid of the caller if this thread is currently executing a two-way binder
|
|
* transaction. Not set if this thread is currently not executing a two way binder transaction.
|
|
*
|
|
* @see #startNotedAppOpsCollection
|
|
* @see #getNotedOpCollectionMode
|
|
*/
|
|
private static final ThreadLocal<Integer> sBinderThreadCallingUid = new ThreadLocal<>();
|
|
|
|
/**
|
|
* If a thread is currently executing a two-way binder transaction, this stores the op-codes of
|
|
* the app-ops that were noted during this transaction.
|
|
*
|
|
* @see #getNotedOpCollectionMode
|
|
* @see #collectNotedOpSync
|
|
*/
|
|
private static final ThreadLocal<ArrayMap<String, BitSet>>
|
|
sAppOpsNotedInThisBinderTransaction = new ThreadLocal<>();
|
|
|
|
static {
|
|
if (sAppOpInfos.length != _NUM_OP) {
|
|
throw new IllegalStateException("mAppOpInfos length " + sAppOpInfos.length
|
|
+ " should be " + _NUM_OP);
|
|
}
|
|
for (int i=0; i<_NUM_OP; i++) {
|
|
if (sAppOpInfos[i].name != null) {
|
|
sOpStrToOp.put(sAppOpInfos[i].name, i);
|
|
}
|
|
}
|
|
for (int op : RUNTIME_PERMISSION_OPS) {
|
|
if (sAppOpInfos[op].permission != null) {
|
|
sPermToOp.put(sAppOpInfos[op].permission, op);
|
|
}
|
|
}
|
|
for (int op : APP_OP_PERMISSION_PACKAGE_OPS) {
|
|
if (sAppOpInfos[op].permission != null) {
|
|
sPermToOp.put(sAppOpInfos[op].permission, op);
|
|
}
|
|
}
|
|
for (int op : APP_OP_PERMISSION_UID_OPS) {
|
|
if (sAppOpInfos[op].permission != null) {
|
|
sPermToOp.put(sAppOpInfos[op].permission, op);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Config used to control app ops access messages sampling */
|
|
private static MessageSamplingConfig sConfig =
|
|
new MessageSamplingConfig(OP_NONE, 0, 0);
|
|
|
|
/** @hide */
|
|
public static final String KEY_HISTORICAL_OPS = "historical_ops";
|
|
|
|
/** System properties for debug logging of noteOp call sites */
|
|
private static final String DEBUG_LOGGING_ENABLE_PROP = "appops.logging_enabled";
|
|
private static final String DEBUG_LOGGING_PACKAGES_PROP = "appops.logging_packages";
|
|
private static final String DEBUG_LOGGING_OPS_PROP = "appops.logging_ops";
|
|
private static final String DEBUG_LOGGING_TAG = "AppOpsManager";
|
|
|
|
/**
|
|
* Retrieve the op switch that controls the given operation.
|
|
* @hide
|
|
*/
|
|
@UnsupportedAppUsage
|
|
public static int opToSwitch(int op) {
|
|
return sAppOpInfos[op].switchCode;
|
|
}
|
|
|
|
/**
|
|
* Retrieve a non-localized name for the operation, for debugging output.
|
|
* @hide
|
|
*/
|
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
|
public static String opToName(int op) {
|
|
if (op == OP_NONE) return "NONE";
|
|
return op < sAppOpInfos.length ? sAppOpInfos[op].simpleName : ("Unknown(" + op + ")");
|
|
}
|
|
|
|
/**
|
|
* Retrieve a non-localized public name for the operation.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static @NonNull String opToPublicName(int op) {
|
|
return sAppOpInfos[op].name;
|
|
}
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
public static int strDebugOpToOp(String op) {
|
|
for (int i = 0; i < sAppOpInfos.length; i++) {
|
|
if (sAppOpInfos[i].simpleName.equals(op)) {
|
|
return i;
|
|
}
|
|
}
|
|
throw new IllegalArgumentException("Unknown operation string: " + op);
|
|
}
|
|
|
|
/**
|
|
* Retrieve the permission associated with an operation, or null if there is not one.
|
|
* @hide
|
|
*/
|
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
|
@TestApi
|
|
public static String opToPermission(int op) {
|
|
return sAppOpInfos[op].permission;
|
|
}
|
|
|
|
/**
|
|
* Retrieve the permission associated with an operation, or null if there is not one.
|
|
|
|
* @param op The operation name.
|
|
*
|
|
* @hide
|
|
*/
|
|
@Nullable
|
|
@SystemApi
|
|
public static String opToPermission(@NonNull String op) {
|
|
return opToPermission(strOpToOp(op));
|
|
}
|
|
|
|
/**
|
|
* Retrieve the user restriction associated with an operation, or null if there is not one.
|
|
* @hide
|
|
*/
|
|
public static String opToRestriction(int op) {
|
|
return sAppOpInfos[op].restriction;
|
|
}
|
|
|
|
/**
|
|
* Retrieve the app op code for a permission, or null if there is not one.
|
|
* This API is intended to be used for mapping runtime or appop permissions
|
|
* to the corresponding app op.
|
|
* @hide
|
|
*/
|
|
@UnsupportedAppUsage
|
|
@TestApi
|
|
public static int permissionToOpCode(String permission) {
|
|
Integer boxedOpCode = sPermToOp.get(permission);
|
|
if (boxedOpCode != null) {
|
|
return boxedOpCode;
|
|
}
|
|
if (permission != null && HealthConnectManager.isHealthPermission(
|
|
ActivityThread.currentApplication(), permission)) {
|
|
return OP_READ_WRITE_HEALTH_DATA;
|
|
}
|
|
return OP_NONE;
|
|
}
|
|
|
|
/**
|
|
* Retrieve whether the op allows to bypass the user restriction.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static RestrictionBypass opAllowSystemBypassRestriction(int op) {
|
|
return sAppOpInfos[op].allowSystemRestrictionBypass;
|
|
}
|
|
|
|
/**
|
|
* Retrieve the default mode for the operation.
|
|
* @hide
|
|
*/
|
|
public static @Mode int opToDefaultMode(int op) {
|
|
if (op == OP_TAKE_AUDIO_FOCUS && roForegroundAudioControl()) {
|
|
// when removing the flag, change the entry in sAppOpInfos for OP_TAKE_AUDIO_FOCUS
|
|
return AppOpsManager.MODE_FOREGROUND;
|
|
}
|
|
return sAppOpInfos[op].defaultMode;
|
|
}
|
|
|
|
/**
|
|
* Retrieve the default mode for the app op.
|
|
*
|
|
* @param appOp The app op name
|
|
*
|
|
* @return the default mode for the app op
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public static int opToDefaultMode(@NonNull String appOp) {
|
|
return opToDefaultMode(strOpToOp(appOp));
|
|
}
|
|
|
|
/**
|
|
* Retrieve the human readable mode.
|
|
* @hide
|
|
*/
|
|
public static String modeToName(@Mode int mode) {
|
|
if (mode >= 0 && mode < MODE_NAMES.length) {
|
|
return MODE_NAMES[mode];
|
|
}
|
|
return "mode=" + mode;
|
|
}
|
|
|
|
/**
|
|
* Retrieve whether the op can be read by apps with privileged appops permission.
|
|
* @hide
|
|
*/
|
|
public static boolean opRestrictsRead(int op) {
|
|
return sAppOpInfos[op].restrictRead;
|
|
}
|
|
|
|
/**
|
|
* Retrieve whether the op allows itself to be reset.
|
|
* @hide
|
|
*/
|
|
public static boolean opAllowsReset(int op) {
|
|
return !sAppOpInfos[op].disableReset;
|
|
}
|
|
|
|
/**
|
|
* Retrieve whether the op is a per-package op for an app op permission.
|
|
* @hide
|
|
*/
|
|
public static boolean opIsPackageAppOpPermission(int op) {
|
|
return ArrayUtils.contains(APP_OP_PERMISSION_PACKAGE_OPS, op);
|
|
}
|
|
|
|
/**
|
|
* Retrieve whether the op is a per-package op for an app op permission.
|
|
* @hide
|
|
*/
|
|
public static boolean opIsUidAppOpPermission(int op) {
|
|
return ArrayUtils.contains(APP_OP_PERMISSION_UID_OPS, op);
|
|
}
|
|
|
|
/**
|
|
* Returns a listenerId suitable for use with {@link #noteOp(int, int, String, String, String)}.
|
|
*
|
|
* This is intended for use client side, when the receiver id must be created before the
|
|
* associated call is made to the system server. If using {@link PendingIntent} as the receiver,
|
|
* avoid using this method as it will include a pointless additional x-process call. Instead
|
|
* prefer passing the PendingIntent to the system server, and then invoking
|
|
* {@link #toReceiverId(PendingIntent)}.
|
|
*
|
|
* @param obj the receiver in use
|
|
* @return a string representation of the receiver suitable for app ops use
|
|
* @hide
|
|
*/
|
|
// TODO: this should probably be @SystemApi as well
|
|
public static @NonNull String toReceiverId(@Nullable Object obj) {
|
|
if (obj == null) {
|
|
return "null";
|
|
} else if (obj instanceof PendingIntent) {
|
|
return toReceiverId((PendingIntent) obj);
|
|
} else {
|
|
return obj.getClass().getName() + "@" + System.identityHashCode(obj);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns a listenerId suitable for use with {@link #noteOp(int, int, String, String, String)}.
|
|
*
|
|
* This is intended for use server side, where ActivityManagerService can be referenced without
|
|
* an additional x-process call.
|
|
*
|
|
* @param pendingIntent the pendingIntent in use
|
|
* @return a string representation of the pending intent suitable for app ops use
|
|
* @see #toReceiverId(Object)
|
|
* @hide
|
|
*/
|
|
// TODO: this should probably be @SystemApi as well
|
|
public static @NonNull String toReceiverId(@NonNull PendingIntent pendingIntent) {
|
|
return pendingIntent.getTag("");
|
|
}
|
|
|
|
/**
|
|
* When to not enforce {@link #setUserRestriction restrictions}.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static class RestrictionBypass {
|
|
/** Does the app need to be system uid to bypass the restriction */
|
|
public boolean isSystemUid;
|
|
|
|
/** Does the app need to be privileged to bypass the restriction */
|
|
public boolean isPrivileged;
|
|
|
|
/**
|
|
* Does the app need to have the EXEMPT_FROM_AUDIO_RESTRICTIONS permission to bypass the
|
|
* restriction
|
|
*/
|
|
public boolean isRecordAudioRestrictionExcept;
|
|
|
|
public RestrictionBypass(boolean isSystemUid, boolean isPrivileged,
|
|
boolean isRecordAudioRestrictionExcept) {
|
|
this.isSystemUid = isSystemUid;
|
|
this.isPrivileged = isPrivileged;
|
|
this.isRecordAudioRestrictionExcept = isRecordAudioRestrictionExcept;
|
|
}
|
|
|
|
public static RestrictionBypass UNRESTRICTED = new RestrictionBypass(false, true, true);
|
|
}
|
|
|
|
/**
|
|
* Class holding all of the operation information associated with an app.
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public static final class PackageOps implements Parcelable {
|
|
private final String mPackageName;
|
|
private final int mUid;
|
|
private final List<OpEntry> mEntries;
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
@UnsupportedAppUsage
|
|
public PackageOps(String packageName, int uid, List<OpEntry> entries) {
|
|
mPackageName = packageName;
|
|
mUid = uid;
|
|
mEntries = entries;
|
|
}
|
|
|
|
/**
|
|
* @return The name of the package.
|
|
*/
|
|
public @NonNull String getPackageName() {
|
|
return mPackageName;
|
|
}
|
|
|
|
/**
|
|
* @return The uid of the package.
|
|
*/
|
|
public int getUid() {
|
|
return mUid;
|
|
}
|
|
|
|
/**
|
|
* @return The ops of the package.
|
|
*/
|
|
public @NonNull List<OpEntry> getOps() {
|
|
return mEntries;
|
|
}
|
|
|
|
@Override
|
|
public int describeContents() {
|
|
return 0;
|
|
}
|
|
|
|
@Override
|
|
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
|
dest.writeString(mPackageName);
|
|
dest.writeInt(mUid);
|
|
dest.writeInt(mEntries.size());
|
|
for (int i=0; i<mEntries.size(); i++) {
|
|
mEntries.get(i).writeToParcel(dest, flags);
|
|
}
|
|
}
|
|
|
|
PackageOps(Parcel source) {
|
|
mPackageName = source.readString();
|
|
mUid = source.readInt();
|
|
mEntries = new ArrayList<OpEntry>();
|
|
final int N = source.readInt();
|
|
for (int i=0; i<N; i++) {
|
|
mEntries.add(OpEntry.CREATOR.createFromParcel(source));
|
|
}
|
|
}
|
|
|
|
public static final @android.annotation.NonNull Creator<PackageOps> CREATOR =
|
|
new Creator<PackageOps>() {
|
|
@Override public PackageOps createFromParcel(Parcel source) {
|
|
return new PackageOps(source);
|
|
}
|
|
|
|
@Override public PackageOps[] newArray(int size) {
|
|
return new PackageOps[size];
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Proxy information for a {@link #noteOp} event
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
// @DataClass(genHiddenConstructor = true, genHiddenCopyConstructor = true)
|
|
// genHiddenCopyConstructor does not work for @hide @SystemApi classes
|
|
public static final class OpEventProxyInfo implements Parcelable {
|
|
/** UID of the proxy app that noted the op */
|
|
private @IntRange(from = 0) int mUid;
|
|
/** Package of the proxy that noted the op */
|
|
private @Nullable String mPackageName;
|
|
/** Attribution tag of the proxy that noted the op */
|
|
private @Nullable String mAttributionTag;
|
|
/** Persistent device Id of the proxy that noted the op */
|
|
private @Nullable String mDeviceId;
|
|
|
|
/**
|
|
* Reinit existing object with new state.
|
|
*
|
|
* @param uid UID of the proxy app that noted the op
|
|
* @param packageName Package of the proxy that noted the op
|
|
* @param attributionTag attribution tag of the proxy that noted the op
|
|
* @param deviceId Persistent device Id of the proxy that noted the op
|
|
*
|
|
* @hide
|
|
*/
|
|
public void reinit(@IntRange(from = 0) int uid, @Nullable String packageName,
|
|
@Nullable String attributionTag, @Nullable String deviceId) {
|
|
mUid = Preconditions.checkArgumentNonnegative(uid);
|
|
mPackageName = packageName;
|
|
mAttributionTag = attributionTag;
|
|
mDeviceId = deviceId;
|
|
}
|
|
|
|
|
|
|
|
// Code below generated by codegen v1.0.14.
|
|
//
|
|
// DO NOT MODIFY!
|
|
// CHECKSTYLE:OFF Generated code
|
|
//
|
|
// To regenerate run:
|
|
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/app/AppOpsManager.java
|
|
//
|
|
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
|
|
// Settings > Editor > Code Style > Formatter Control
|
|
//@formatter:off
|
|
|
|
|
|
/**
|
|
* Creates a new OpEventProxyInfo.
|
|
*
|
|
* @param uid
|
|
* UID of the proxy app that noted the op
|
|
* @param packageName
|
|
* Package of the proxy that noted the op
|
|
* @param attributionTag
|
|
* Attribution tag of the proxy that noted the op
|
|
* @hide
|
|
*/
|
|
@DataClass.Generated.Member
|
|
public OpEventProxyInfo(
|
|
@IntRange(from = 0) int uid,
|
|
@Nullable String packageName,
|
|
@Nullable String attributionTag) {
|
|
this(uid, packageName, attributionTag,
|
|
VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT);
|
|
}
|
|
|
|
/**
|
|
* Creates a new OpEventProxyInfo.
|
|
*
|
|
* @param uid UID of the proxy app that noted the op
|
|
* @param packageName Package of the proxy that noted the op
|
|
* @param attributionTag Attribution tag of the proxy that noted the op
|
|
* @param deviceId Persistent device Id of the proxy that noted the op
|
|
*
|
|
* @hide
|
|
*/
|
|
public OpEventProxyInfo(
|
|
@IntRange(from = 0) int uid,
|
|
@Nullable String packageName,
|
|
@Nullable String attributionTag,
|
|
@Nullable String deviceId) {
|
|
this.mUid = uid;
|
|
com.android.internal.util.AnnotationValidations.validate(
|
|
IntRange.class, null, mUid,
|
|
"from", 0);
|
|
this.mPackageName = packageName;
|
|
this.mAttributionTag = attributionTag;
|
|
this.mDeviceId = deviceId;
|
|
}
|
|
/**
|
|
* Copy constructor
|
|
*
|
|
* @hide
|
|
*/
|
|
@DataClass.Generated.Member
|
|
public OpEventProxyInfo(@NonNull OpEventProxyInfo orig) {
|
|
mUid = orig.mUid;
|
|
mPackageName = orig.mPackageName;
|
|
mAttributionTag = orig.mAttributionTag;
|
|
mDeviceId = orig.mDeviceId;
|
|
}
|
|
|
|
/**
|
|
* UID of the proxy app that noted the op
|
|
*/
|
|
@DataClass.Generated.Member
|
|
public @IntRange(from = 0) int getUid() {
|
|
return mUid;
|
|
}
|
|
|
|
/**
|
|
* Package of the proxy that noted the op
|
|
*/
|
|
@DataClass.Generated.Member
|
|
public @Nullable String getPackageName() {
|
|
return mPackageName;
|
|
}
|
|
|
|
/**
|
|
* Attribution tag of the proxy that noted the op
|
|
*/
|
|
@DataClass.Generated.Member
|
|
public @Nullable String getAttributionTag() {
|
|
return mAttributionTag;
|
|
}
|
|
|
|
@FlaggedApi(Flags.FLAG_DEVICE_ID_IN_OP_PROXY_INFO_ENABLED)
|
|
public @Nullable String getDeviceId() { return mDeviceId; }
|
|
|
|
@Override
|
|
@DataClass.Generated.Member
|
|
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
|
// You can override field parcelling by defining methods like:
|
|
// void parcelFieldName(Parcel dest, int flags) { ... }
|
|
|
|
byte flg = 0;
|
|
if (mPackageName != null) flg |= 0x2;
|
|
if (mAttributionTag != null) flg |= 0x4;
|
|
if (mDeviceId != null) flg |= 0x8;
|
|
dest.writeByte(flg);
|
|
dest.writeInt(mUid);
|
|
if (mPackageName != null) dest.writeString(mPackageName);
|
|
if (mAttributionTag != null) dest.writeString(mAttributionTag);
|
|
if (mDeviceId != null) dest.writeString(mDeviceId);
|
|
}
|
|
|
|
@Override
|
|
@DataClass.Generated.Member
|
|
public int describeContents() { return 0; }
|
|
|
|
/** @hide */
|
|
@SuppressWarnings({"unchecked", "RedundantCast"})
|
|
@DataClass.Generated.Member
|
|
/* package-private */ OpEventProxyInfo(@NonNull Parcel in) {
|
|
// You can override field unparcelling by defining methods like:
|
|
// static FieldType unparcelFieldName(Parcel in) { ... }
|
|
|
|
byte flg = in.readByte();
|
|
int uid = in.readInt();
|
|
String packageName = (flg & 0x2) == 0 ? null : in.readString();
|
|
String attributionTag = (flg & 0x4) == 0 ? null : in.readString();
|
|
String deviceId = (flg & 0x8) == 0 ? null : in.readString();
|
|
this.mUid = uid;
|
|
com.android.internal.util.AnnotationValidations.validate(
|
|
IntRange.class, null, mUid,
|
|
"from", 0);
|
|
this.mPackageName = packageName;
|
|
this.mAttributionTag = attributionTag;
|
|
this.mDeviceId = deviceId;
|
|
// onConstructed(); // You can define this method to get a callback
|
|
}
|
|
|
|
@DataClass.Generated.Member
|
|
public static final @NonNull Parcelable.Creator<OpEventProxyInfo> CREATOR
|
|
= new Parcelable.Creator<OpEventProxyInfo>() {
|
|
@Override
|
|
public OpEventProxyInfo[] newArray(int size) {
|
|
return new OpEventProxyInfo[size];
|
|
}
|
|
|
|
@Override
|
|
public OpEventProxyInfo createFromParcel(@NonNull Parcel in) {
|
|
return new OpEventProxyInfo(in);
|
|
}
|
|
};
|
|
|
|
/*
|
|
@DataClass.Generated(
|
|
time = 1576814974615L,
|
|
codegenVersion = "1.0.14",
|
|
sourceFile = "frameworks/base/core/java/android/app/AppOpsManager.java",
|
|
inputSignatures = "private @android.annotation.IntRange(from=0L) int mUid\nprivate @android.annotation.Nullable java.lang.String mPackageName\nprivate @android.annotation.Nullable java.lang.String mAttributionTag\npublic void reinit(int,java.lang.String,java.lang.String)\nclass OpEventProxyInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genHiddenCopyConstructor=true)")
|
|
@Deprecated
|
|
private void __metadata() {}
|
|
*/
|
|
|
|
//@formatter:on
|
|
// End of generated code
|
|
|
|
}
|
|
|
|
/**
|
|
* Description of a {@link #noteOp} or {@link #startOp} event
|
|
*
|
|
* @hide
|
|
*/
|
|
//@DataClass codegen verifier is broken
|
|
public static final class NoteOpEvent implements Parcelable {
|
|
/** Time of noteOp event */
|
|
private @IntRange(from = 0) long mNoteTime;
|
|
/** The duration of this event (in case this is a startOp event, -1 otherwise). */
|
|
private @IntRange(from = -1) long mDuration;
|
|
/** Proxy information of the noteOp event */
|
|
private @Nullable OpEventProxyInfo mProxy;
|
|
|
|
/**
|
|
* Reinit existing object with new state.
|
|
*
|
|
* @param noteTime Time of noteOp event
|
|
* @param duration The duration of this event (in case this is a startOp event,
|
|
* -1 otherwise).
|
|
* @param proxy Proxy information of the noteOp event
|
|
* @param proxyPool The pool to release previous {@link OpEventProxyInfo} to
|
|
*/
|
|
public void reinit(@IntRange(from = 0) long noteTime,
|
|
@IntRange(from = -1) long duration,
|
|
@Nullable OpEventProxyInfo proxy,
|
|
@NonNull Pools.Pool<OpEventProxyInfo> proxyPool) {
|
|
mNoteTime = Preconditions.checkArgumentNonnegative(noteTime);
|
|
mDuration = Preconditions.checkArgumentInRange(duration, -1L, Long.MAX_VALUE,
|
|
"duration");
|
|
|
|
if (mProxy != null) {
|
|
proxyPool.release(mProxy);
|
|
}
|
|
mProxy = proxy;
|
|
}
|
|
|
|
/**
|
|
* Copy constructor
|
|
*
|
|
* @hide
|
|
*/
|
|
public NoteOpEvent(@NonNull NoteOpEvent original) {
|
|
this(original.mNoteTime, original.mDuration,
|
|
original.mProxy != null ? new OpEventProxyInfo(original.mProxy) : null);
|
|
}
|
|
|
|
|
|
|
|
// Code below generated by codegen v1.0.14.
|
|
//
|
|
// DO NOT MODIFY!
|
|
// CHECKSTYLE:OFF Generated code
|
|
//
|
|
// To regenerate run:
|
|
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/app/AppOpsManager.java
|
|
//
|
|
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
|
|
// Settings > Editor > Code Style > Formatter Control
|
|
//@formatter:off
|
|
|
|
|
|
/**
|
|
* Creates a new NoteOpEvent.
|
|
*
|
|
* @param noteTime
|
|
* Time of noteOp event
|
|
* @param duration
|
|
* The duration of this event (in case this is a startOp event, -1 otherwise).
|
|
* @param proxy
|
|
* Proxy information of the noteOp event
|
|
*/
|
|
@DataClass.Generated.Member
|
|
public NoteOpEvent(
|
|
@IntRange(from = 0) long noteTime,
|
|
@IntRange(from = -1) long duration,
|
|
@Nullable OpEventProxyInfo proxy) {
|
|
this.mNoteTime = noteTime;
|
|
com.android.internal.util.AnnotationValidations.validate(
|
|
IntRange.class, null, mNoteTime,
|
|
"from", 0);
|
|
this.mDuration = duration;
|
|
com.android.internal.util.AnnotationValidations.validate(
|
|
IntRange.class, null, mDuration,
|
|
"from", -1);
|
|
this.mProxy = proxy;
|
|
|
|
// onConstructed(); // You can define this method to get a callback
|
|
}
|
|
|
|
/**
|
|
* Time of noteOp event
|
|
*/
|
|
@DataClass.Generated.Member
|
|
public @IntRange(from = 0) long getNoteTime() {
|
|
return mNoteTime;
|
|
}
|
|
|
|
/**
|
|
* The duration of this event (in case this is a startOp event, -1 otherwise).
|
|
*/
|
|
@DataClass.Generated.Member
|
|
public @IntRange(from = -1) long getDuration() {
|
|
return mDuration;
|
|
}
|
|
|
|
/**
|
|
* Proxy information of the noteOp event
|
|
*/
|
|
@DataClass.Generated.Member
|
|
public @Nullable OpEventProxyInfo getProxy() {
|
|
return mProxy;
|
|
}
|
|
|
|
@Override
|
|
@DataClass.Generated.Member
|
|
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
|
// You can override field parcelling by defining methods like:
|
|
// void parcelFieldName(Parcel dest, int flags) { ... }
|
|
|
|
byte flg = 0;
|
|
if (mProxy != null) flg |= 0x4;
|
|
dest.writeByte(flg);
|
|
dest.writeLong(mNoteTime);
|
|
dest.writeLong(mDuration);
|
|
if (mProxy != null) dest.writeTypedObject(mProxy, flags);
|
|
}
|
|
|
|
@Override
|
|
@DataClass.Generated.Member
|
|
public int describeContents() { return 0; }
|
|
|
|
/** @hide */
|
|
@SuppressWarnings({"unchecked", "RedundantCast"})
|
|
@DataClass.Generated.Member
|
|
/* package-private */ NoteOpEvent(@NonNull Parcel in) {
|
|
// You can override field unparcelling by defining methods like:
|
|
// static FieldType unparcelFieldName(Parcel in) { ... }
|
|
|
|
byte flg = in.readByte();
|
|
long noteTime = in.readLong();
|
|
long duration = in.readLong();
|
|
OpEventProxyInfo proxy = (flg & 0x4) == 0 ? null : (OpEventProxyInfo) in.readTypedObject(OpEventProxyInfo.CREATOR);
|
|
|
|
this.mNoteTime = noteTime;
|
|
com.android.internal.util.AnnotationValidations.validate(
|
|
IntRange.class, null, mNoteTime,
|
|
"from", 0);
|
|
this.mDuration = duration;
|
|
com.android.internal.util.AnnotationValidations.validate(
|
|
IntRange.class, null, mDuration,
|
|
"from", -1);
|
|
this.mProxy = proxy;
|
|
|
|
// onConstructed(); // You can define this method to get a callback
|
|
}
|
|
|
|
@DataClass.Generated.Member
|
|
public static final @NonNull Parcelable.Creator<NoteOpEvent> CREATOR
|
|
= new Parcelable.Creator<NoteOpEvent>() {
|
|
@Override
|
|
public NoteOpEvent[] newArray(int size) {
|
|
return new NoteOpEvent[size];
|
|
}
|
|
|
|
@Override
|
|
public NoteOpEvent createFromParcel(@NonNull Parcel in) {
|
|
return new NoteOpEvent(in);
|
|
}
|
|
};
|
|
|
|
/*
|
|
@DataClass.Generated(
|
|
time = 1576811792274L,
|
|
codegenVersion = "1.0.14",
|
|
sourceFile = "frameworks/base/core/java/android/app/AppOpsManager.java",
|
|
inputSignatures = "private @android.annotation.IntRange(from=0L) long mNoteTime\nprivate @android.annotation.IntRange(from=-1) long mDuration\nprivate @android.annotation.Nullable android.app.OpEventProxyInfo mProxy\npublic void reinit(long,long,android.app.OpEventProxyInfo,android.util.Pools.Pool<android.app.OpEventProxyInfo>)\npublic @java.lang.Override java.lang.Object clone()\nclass NoteOpEvent extends java.lang.Object implements [android.os.Parcelable, java.lang.Cloneable]\n@com.android.internal.util.DataClass")
|
|
@Deprecated
|
|
private void __metadata() {}
|
|
*/
|
|
|
|
|
|
//@formatter:on
|
|
// End of generated code
|
|
|
|
}
|
|
|
|
/**
|
|
* Last {@link #noteOp} and {@link #startOp} events performed for a single op and a specific
|
|
* {@link Context#createAttributionContext(String) attribution} for all uidModes and opFlags.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
@Immutable
|
|
// @DataClass(genHiddenConstructor = true) codegen verifier is broken
|
|
@DataClass.Suppress({"getAccessEvents", "getRejectEvents", "getOp"})
|
|
public static final class AttributedOpEntry implements Parcelable {
|
|
/** The code of the op */
|
|
private final @IntRange(from = 0, to = _NUM_OP - 1) int mOp;
|
|
/** Whether the op is running */
|
|
private final boolean mRunning;
|
|
/** The access events */
|
|
@DataClass.ParcelWith(LongSparseArrayParceling.class)
|
|
private final @Nullable LongSparseArray<NoteOpEvent> mAccessEvents;
|
|
/** The rejection events */
|
|
@DataClass.ParcelWith(LongSparseArrayParceling.class)
|
|
private final @Nullable LongSparseArray<NoteOpEvent> mRejectEvents;
|
|
|
|
private AttributedOpEntry(@NonNull AttributedOpEntry other) {
|
|
mOp = other.mOp;
|
|
mRunning = other.mRunning;
|
|
mAccessEvents = other.mAccessEvents == null ? null : other.mAccessEvents.clone();
|
|
mRejectEvents = other.mRejectEvents == null ? null : other.mRejectEvents.clone();
|
|
}
|
|
|
|
/**
|
|
* Returns all keys for which we have events.
|
|
*
|
|
* @hide
|
|
*/
|
|
public @NonNull ArraySet<Long> collectKeys() {
|
|
ArraySet<Long> keys = new ArraySet<>();
|
|
|
|
if (mAccessEvents != null) {
|
|
int numEvents = mAccessEvents.size();
|
|
for (int i = 0; i < numEvents; i++) {
|
|
keys.add(mAccessEvents.keyAt(i));
|
|
}
|
|
}
|
|
|
|
if (mRejectEvents != null) {
|
|
int numEvents = mRejectEvents.size();
|
|
for (int i = 0; i < numEvents; i++) {
|
|
keys.add(mRejectEvents.keyAt(i));
|
|
}
|
|
}
|
|
|
|
return keys;
|
|
}
|
|
|
|
/**
|
|
* Return the last access time.
|
|
*
|
|
* @param flags The op flags
|
|
*
|
|
* @return the last access time (in milliseconds since epoch start (January 1, 1970
|
|
* 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no access
|
|
*
|
|
* @see #getLastAccessForegroundTime(int)
|
|
* @see #getLastAccessBackgroundTime(int)
|
|
* @see #getLastAccessTime(int, int, int)
|
|
* @see OpEntry#getLastAccessTime(int)
|
|
*/
|
|
public long getLastAccessTime(@OpFlags int flags) {
|
|
return getLastAccessTime(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, flags);
|
|
}
|
|
|
|
/**
|
|
* Return the last foreground access time.
|
|
*
|
|
* @param flags The op flags
|
|
*
|
|
* @return the last access time (in milliseconds since epoch start (January 1, 1970
|
|
* 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no foreground access
|
|
*
|
|
* @see #getLastAccessTime(int)
|
|
* @see #getLastAccessBackgroundTime(int)
|
|
* @see #getLastAccessTime(int, int, int)
|
|
* @see OpEntry#getLastAccessForegroundTime(int)
|
|
*/
|
|
public long getLastAccessForegroundTime(@OpFlags int flags) {
|
|
return getLastAccessTime(MAX_PRIORITY_UID_STATE, resolveFirstUnrestrictedUidState(mOp),
|
|
flags);
|
|
}
|
|
|
|
/**
|
|
* Return the last background access time.
|
|
*
|
|
* @param flags The op flags
|
|
*
|
|
* @return the last access time (in milliseconds since epoch start (January 1, 1970
|
|
* 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no background access
|
|
*
|
|
* @see #getLastAccessTime(int)
|
|
* @see #getLastAccessForegroundTime(int)
|
|
* @see #getLastAccessTime(int, int, int)
|
|
* @see OpEntry#getLastAccessBackgroundTime(int)
|
|
*/
|
|
public long getLastAccessBackgroundTime(@OpFlags int flags) {
|
|
return getLastAccessTime(resolveLastRestrictedUidState(mOp), MIN_PRIORITY_UID_STATE,
|
|
flags);
|
|
}
|
|
|
|
/**
|
|
* Return the last access event.
|
|
*
|
|
* @param flags The op flags
|
|
*
|
|
* @return the last access event of {@code null} if there was no access
|
|
*/
|
|
private @Nullable NoteOpEvent getLastAccessEvent(@UidState int fromUidState,
|
|
@UidState int toUidState, @OpFlags int flags) {
|
|
return getLastEvent(mAccessEvents, fromUidState, toUidState, flags);
|
|
}
|
|
|
|
/**
|
|
* Return the last access time.
|
|
*
|
|
* @param fromUidState The lowest UID state for which to query
|
|
* @param toUidState The highest UID state for which to query (inclusive)
|
|
* @param flags The op flags
|
|
*
|
|
* @return the last access time (in milliseconds since epoch start (January 1, 1970
|
|
* 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no access
|
|
*
|
|
* @see #getLastAccessTime(int)
|
|
* @see #getLastAccessForegroundTime(int)
|
|
* @see #getLastAccessBackgroundTime(int)
|
|
* @see OpEntry#getLastAccessTime(int, int, int)
|
|
*/
|
|
public long getLastAccessTime(@UidState int fromUidState, @UidState int toUidState,
|
|
@OpFlags int flags) {
|
|
NoteOpEvent lastEvent = getLastAccessEvent(fromUidState, toUidState, flags);
|
|
if (lastEvent == null) {
|
|
return -1;
|
|
}
|
|
|
|
return lastEvent.getNoteTime();
|
|
}
|
|
|
|
/**
|
|
* Return the last rejection time.
|
|
*
|
|
* @param flags The op flags
|
|
*
|
|
* @return the last rejection time (in milliseconds since epoch start (January 1, 1970
|
|
* 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no rejection
|
|
*
|
|
* @see #getLastRejectForegroundTime(int)
|
|
* @see #getLastRejectBackgroundTime(int)
|
|
* @see #getLastRejectTime(int, int, int)
|
|
* @see OpEntry#getLastRejectTime(int)
|
|
*/
|
|
public long getLastRejectTime(@OpFlags int flags) {
|
|
return getLastRejectTime(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, flags);
|
|
}
|
|
|
|
/**
|
|
* Return the last foreground rejection time.
|
|
*
|
|
* @param flags The op flags
|
|
*
|
|
* @return the last rejection time (in milliseconds since epoch start (January 1, 1970
|
|
* 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no foreground rejection
|
|
*
|
|
* @see #getLastRejectTime(int)
|
|
* @see #getLastRejectBackgroundTime(int)
|
|
* @see #getLastRejectTime(int, int, int)
|
|
* @see OpEntry#getLastRejectForegroundTime(int)
|
|
*/
|
|
public long getLastRejectForegroundTime(@OpFlags int flags) {
|
|
return getLastRejectTime(MAX_PRIORITY_UID_STATE, resolveFirstUnrestrictedUidState(mOp),
|
|
flags);
|
|
}
|
|
|
|
/**
|
|
* Return the last background rejection time.
|
|
*
|
|
* @param flags The op flags
|
|
*
|
|
* @return the last rejection time (in milliseconds since epoch start (January 1, 1970
|
|
* 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no background rejection
|
|
*
|
|
* @see #getLastRejectTime(int)
|
|
* @see #getLastRejectForegroundTime(int)
|
|
* @see #getLastRejectTime(int, int, int)
|
|
* @see OpEntry#getLastRejectBackgroundTime(int)
|
|
*/
|
|
public long getLastRejectBackgroundTime(@OpFlags int flags) {
|
|
return getLastRejectTime(resolveLastRestrictedUidState(mOp), MIN_PRIORITY_UID_STATE,
|
|
flags);
|
|
}
|
|
|
|
/**
|
|
* Return the last background rejection event.
|
|
*
|
|
* @param flags The op flags
|
|
*
|
|
* @return the last rejection event of {@code null} if there was no rejection
|
|
*
|
|
* @see #getLastRejectTime(int)
|
|
* @see #getLastRejectForegroundTime(int)
|
|
* @see #getLastRejectBackgroundTime(int)
|
|
* @see OpEntry#getLastRejectTime(int, int, int)
|
|
*/
|
|
private @Nullable NoteOpEvent getLastRejectEvent(@UidState int fromUidState,
|
|
@UidState int toUidState, @OpFlags int flags) {
|
|
return getLastEvent(mRejectEvents, fromUidState, toUidState, flags);
|
|
}
|
|
|
|
/**
|
|
* Return the last rejection time.
|
|
*
|
|
* @param fromUidState The lowest UID state for which to query
|
|
* @param toUidState The highest UID state for which to query (inclusive)
|
|
* @param flags The op flags
|
|
*
|
|
* @return the last access time (in milliseconds since epoch) or {@code -1} if there was no
|
|
* rejection
|
|
*
|
|
* @see #getLastRejectTime(int)
|
|
* @see #getLastRejectForegroundTime(int)
|
|
* @see #getLastRejectForegroundTime(int)
|
|
* @see #getLastRejectTime(int, int, int)
|
|
* @see OpEntry#getLastRejectTime(int, int, int)
|
|
*/
|
|
public long getLastRejectTime(@UidState int fromUidState, @UidState int toUidState,
|
|
@OpFlags int flags) {
|
|
NoteOpEvent lastEvent = getLastRejectEvent(fromUidState, toUidState, flags);
|
|
if (lastEvent == null) {
|
|
return -1;
|
|
}
|
|
|
|
return lastEvent.getNoteTime();
|
|
}
|
|
|
|
/**
|
|
* Return the duration in milliseconds of the last the access.
|
|
*
|
|
* @param flags The op flags
|
|
*
|
|
* @return the duration in milliseconds or {@code -1} if there was no rejection
|
|
*
|
|
* @see #getLastForegroundDuration(int)
|
|
* @see #getLastBackgroundDuration(int)
|
|
* @see #getLastDuration(int, int, int)
|
|
* @see OpEntry#getLastDuration(int)
|
|
*/
|
|
public long getLastDuration(@OpFlags int flags) {
|
|
return getLastDuration(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, flags);
|
|
}
|
|
|
|
/**
|
|
* Return the duration in milliseconds of the last foreground access.
|
|
*
|
|
* @param flags The op flags
|
|
*
|
|
* @return the duration in milliseconds or {@code -1} if there was no foreground rejection
|
|
*
|
|
* @see #getLastDuration(int)
|
|
* @see #getLastBackgroundDuration(int)
|
|
* @see #getLastDuration(int, int, int)
|
|
* @see OpEntry#getLastForegroundDuration(int)
|
|
*/
|
|
public long getLastForegroundDuration(@OpFlags int flags) {
|
|
return getLastDuration(MAX_PRIORITY_UID_STATE, resolveFirstUnrestrictedUidState(mOp),
|
|
flags);
|
|
}
|
|
|
|
/**
|
|
* Return the duration in milliseconds of the last background access.
|
|
*
|
|
* @param flags The op flags
|
|
*
|
|
* @return the duration in milliseconds or {@code -1} if there was no background rejection
|
|
*
|
|
* @see #getLastDuration(int)
|
|
* @see #getLastForegroundDuration(int)
|
|
* @see #getLastDuration(int, int, int)
|
|
* @see OpEntry#getLastBackgroundDuration(int)
|
|
*/
|
|
public long getLastBackgroundDuration(@OpFlags int flags) {
|
|
return getLastDuration(resolveLastRestrictedUidState(mOp), MIN_PRIORITY_UID_STATE,
|
|
flags);
|
|
}
|
|
|
|
/**
|
|
* Return the duration in milliseconds of the last access.
|
|
*
|
|
* @param fromUidState The lowest UID state for which to query
|
|
* @param toUidState The highest UID state for which to query (inclusive)
|
|
* @param flags The op flags
|
|
*
|
|
* @return the duration in milliseconds or {@code -1} if there was no rejection
|
|
*
|
|
* @see #getLastDuration(int)
|
|
* @see #getLastForegroundDuration(int)
|
|
* @see #getLastBackgroundDuration(int)
|
|
* @see #getLastDuration(int, int, int)
|
|
* @see OpEntry#getLastDuration(int, int, int)
|
|
*/
|
|
public long getLastDuration(@UidState int fromUidState, @UidState int toUidState,
|
|
@OpFlags int flags) {
|
|
NoteOpEvent lastEvent = getLastAccessEvent(fromUidState, toUidState, flags);;
|
|
if (lastEvent == null) {
|
|
return -1;
|
|
}
|
|
|
|
return lastEvent.getDuration();
|
|
}
|
|
|
|
/**
|
|
* Gets the proxy info of the app that performed the last access on behalf of this
|
|
* attribution and as a result blamed the op on this attribution.
|
|
*
|
|
* @param flags The op flags
|
|
*
|
|
* @return The proxy info or {@code null} if there was no proxy access
|
|
*
|
|
* @see #getLastForegroundProxyInfo(int)
|
|
* @see #getLastBackgroundProxyInfo(int)
|
|
* @see #getLastProxyInfo(int, int, int)
|
|
* @see OpEntry#getLastProxyInfo(int)
|
|
*/
|
|
public @Nullable OpEventProxyInfo getLastProxyInfo(@OpFlags int flags) {
|
|
return getLastProxyInfo(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, flags);
|
|
}
|
|
|
|
/**
|
|
* Gets the proxy info of the app that performed the last foreground access on behalf of
|
|
* this attribution and as a result blamed the op on this attribution.
|
|
*
|
|
* @param flags The op flags
|
|
*
|
|
* @return The proxy info or {@code null} if there was no proxy access
|
|
*
|
|
* @see #getLastProxyInfo(int)
|
|
* @see #getLastBackgroundProxyInfo(int)
|
|
* @see #getLastProxyInfo(int, int, int)
|
|
* @see OpEntry#getLastForegroundProxyInfo(int)
|
|
*/
|
|
public @Nullable OpEventProxyInfo getLastForegroundProxyInfo(@OpFlags int flags) {
|
|
return getLastProxyInfo(MAX_PRIORITY_UID_STATE, resolveFirstUnrestrictedUidState(mOp),
|
|
flags);
|
|
}
|
|
|
|
/**
|
|
* Gets the proxy info of the app that performed the last background access on behalf of
|
|
* this attribution and as a result blamed the op on this attribution.
|
|
*
|
|
* @param flags The op flags
|
|
*
|
|
* @return The proxy info or {@code null} if there was no proxy background access
|
|
*
|
|
* @see #getLastProxyInfo(int)
|
|
* @see #getLastForegroundProxyInfo(int)
|
|
* @see #getLastProxyInfo(int, int, int)
|
|
* @see OpEntry#getLastBackgroundProxyInfo(int)
|
|
*/
|
|
public @Nullable OpEventProxyInfo getLastBackgroundProxyInfo(@OpFlags int flags) {
|
|
return getLastProxyInfo(resolveLastRestrictedUidState(mOp), MIN_PRIORITY_UID_STATE,
|
|
flags);
|
|
}
|
|
|
|
/**
|
|
* Gets the proxy info of the app that performed the last access on behalf of this
|
|
* attribution and as a result blamed the op on this attribution.
|
|
*
|
|
* @param fromUidState The lowest UID state for which to query
|
|
* @param toUidState The highest UID state for which to query (inclusive)
|
|
* @param flags The op flags
|
|
*
|
|
* @return The proxy info or {@code null} if there was no proxy foreground access
|
|
*
|
|
* @see #getLastProxyInfo(int)
|
|
* @see #getLastForegroundProxyInfo(int)
|
|
* @see #getLastBackgroundProxyInfo(int)
|
|
* @see OpEntry#getLastProxyInfo(int, int, int)
|
|
*/
|
|
public @Nullable OpEventProxyInfo getLastProxyInfo(@UidState int fromUidState,
|
|
@UidState int toUidState, @OpFlags int flags) {
|
|
NoteOpEvent lastEvent = getLastAccessEvent(fromUidState, toUidState, flags);
|
|
if (lastEvent == null) {
|
|
return null;
|
|
}
|
|
|
|
return lastEvent.getProxy();
|
|
}
|
|
|
|
@NonNull
|
|
String getOpName() {
|
|
return AppOpsManager.opToPublicName(mOp);
|
|
}
|
|
|
|
int getOp() {
|
|
return mOp;
|
|
}
|
|
|
|
private static class LongSparseArrayParceling implements
|
|
Parcelling<LongSparseArray<NoteOpEvent>> {
|
|
@Override
|
|
public void parcel(@Nullable LongSparseArray<NoteOpEvent> array, @NonNull Parcel dest,
|
|
int parcelFlags) {
|
|
if (array == null) {
|
|
dest.writeInt(-1);
|
|
return;
|
|
}
|
|
|
|
int numEntries = array.size();
|
|
dest.writeInt(numEntries);
|
|
|
|
for (int i = 0; i < numEntries; i++) {
|
|
dest.writeLong(array.keyAt(i));
|
|
dest.writeParcelable(array.valueAt(i), parcelFlags);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public @Nullable LongSparseArray<NoteOpEvent> unparcel(@NonNull Parcel source) {
|
|
int numEntries = source.readInt();
|
|
if (numEntries == -1) {
|
|
return null;
|
|
}
|
|
|
|
LongSparseArray<NoteOpEvent> array = new LongSparseArray<>(numEntries);
|
|
|
|
for (int i = 0; i < numEntries; i++) {
|
|
array.put(source.readLong(), source.readParcelable(null, android.app.AppOpsManager.NoteOpEvent.class));
|
|
}
|
|
|
|
return array;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Code below generated by codegen v1.0.14.
|
|
//
|
|
// DO NOT MODIFY!
|
|
// CHECKSTYLE:OFF Generated code
|
|
//
|
|
// To regenerate run:
|
|
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/app/AppOpsManager.java
|
|
//
|
|
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
|
|
// Settings > Editor > Code Style > Formatter Control
|
|
//@formatter:off
|
|
|
|
|
|
/**
|
|
* Creates a new OpAttributionEntry.
|
|
*
|
|
* @param op
|
|
* The code of the op
|
|
* @param running
|
|
* Whether the op is running
|
|
* @param accessEvents
|
|
* The access events
|
|
* @param rejectEvents
|
|
* The rejection events
|
|
* @hide
|
|
*/
|
|
@DataClass.Generated.Member
|
|
public AttributedOpEntry(
|
|
@IntRange(from = 0, to = _NUM_OP - 1) int op,
|
|
boolean running,
|
|
@Nullable LongSparseArray<NoteOpEvent> accessEvents,
|
|
@Nullable LongSparseArray<NoteOpEvent> rejectEvents) {
|
|
this.mOp = op;
|
|
com.android.internal.util.AnnotationValidations.validate(
|
|
IntRange.class, null, mOp,
|
|
"from", 0,
|
|
"to", _NUM_OP - 1);
|
|
this.mRunning = running;
|
|
this.mAccessEvents = accessEvents;
|
|
this.mRejectEvents = rejectEvents;
|
|
|
|
// onConstructed(); // You can define this method to get a callback
|
|
}
|
|
|
|
/**
|
|
* Whether the op is running
|
|
*/
|
|
@DataClass.Generated.Member
|
|
public boolean isRunning() {
|
|
return mRunning;
|
|
}
|
|
|
|
@DataClass.Generated.Member
|
|
static Parcelling<LongSparseArray<NoteOpEvent>> sParcellingForAccessEvents =
|
|
Parcelling.Cache.get(
|
|
LongSparseArrayParceling.class);
|
|
static {
|
|
if (sParcellingForAccessEvents == null) {
|
|
sParcellingForAccessEvents = Parcelling.Cache.put(
|
|
new LongSparseArrayParceling());
|
|
}
|
|
}
|
|
|
|
@DataClass.Generated.Member
|
|
static Parcelling<LongSparseArray<NoteOpEvent>> sParcellingForRejectEvents =
|
|
Parcelling.Cache.get(
|
|
LongSparseArrayParceling.class);
|
|
static {
|
|
if (sParcellingForRejectEvents == null) {
|
|
sParcellingForRejectEvents = Parcelling.Cache.put(
|
|
new LongSparseArrayParceling());
|
|
}
|
|
}
|
|
|
|
@Override
|
|
@DataClass.Generated.Member
|
|
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
|
// You can override field parcelling by defining methods like:
|
|
// void parcelFieldName(Parcel dest, int flags) { ... }
|
|
|
|
byte flg = 0;
|
|
if (mRunning) flg |= 0x2;
|
|
if (mAccessEvents != null) flg |= 0x4;
|
|
if (mRejectEvents != null) flg |= 0x8;
|
|
dest.writeByte(flg);
|
|
dest.writeInt(mOp);
|
|
sParcellingForAccessEvents.parcel(mAccessEvents, dest, flags);
|
|
sParcellingForRejectEvents.parcel(mRejectEvents, dest, flags);
|
|
}
|
|
|
|
@Override
|
|
@DataClass.Generated.Member
|
|
public int describeContents() { return 0; }
|
|
|
|
/** @hide */
|
|
@SuppressWarnings({"unchecked", "RedundantCast"})
|
|
@DataClass.Generated.Member
|
|
/* package-private */ AttributedOpEntry(@NonNull Parcel in) {
|
|
// You can override field unparcelling by defining methods like:
|
|
// static FieldType unparcelFieldName(Parcel in) { ... }
|
|
|
|
byte flg = in.readByte();
|
|
boolean running = (flg & 0x2) != 0;
|
|
int op = in.readInt();
|
|
LongSparseArray<NoteOpEvent> accessEvents = sParcellingForAccessEvents.unparcel(in);
|
|
LongSparseArray<NoteOpEvent> rejectEvents = sParcellingForRejectEvents.unparcel(in);
|
|
|
|
this.mOp = op;
|
|
com.android.internal.util.AnnotationValidations.validate(
|
|
IntRange.class, null, mOp,
|
|
"from", 0,
|
|
"to", _NUM_OP - 1);
|
|
this.mRunning = running;
|
|
this.mAccessEvents = accessEvents;
|
|
this.mRejectEvents = rejectEvents;
|
|
|
|
// onConstructed(); // You can define this method to get a callback
|
|
}
|
|
|
|
@DataClass.Generated.Member
|
|
public static final @NonNull Parcelable.Creator<AttributedOpEntry> CREATOR
|
|
= new Parcelable.Creator<AttributedOpEntry>() {
|
|
@Override
|
|
public AttributedOpEntry[] newArray(int size) {
|
|
return new AttributedOpEntry[size];
|
|
}
|
|
|
|
@Override
|
|
public AttributedOpEntry createFromParcel(@NonNull Parcel in) {
|
|
return new AttributedOpEntry(in);
|
|
}
|
|
};
|
|
|
|
/*
|
|
@DataClass.Generated(
|
|
time = 1574809856239L,
|
|
codegenVersion = "1.0.14",
|
|
sourceFile = "frameworks/base/core/java/android/app/AppOpsManager.java",
|
|
inputSignatures = "private final @android.annotation.IntRange(from=0L, to=_NUM_OP - 1) int mOp\nprivate final boolean mRunning\nprivate final @com.android.internal.util.DataClass.ParcelWith(android.app.OpAttributionEntry.LongSparseArrayParceling.class) @android.annotation.Nullable android.util.LongSparseArray<android.app.NoteOpEvent> mAccessEvents\nprivate final @com.android.internal.util.DataClass.ParcelWith(android.app.OpAttributionEntry.LongSparseArrayParceling.class) @android.annotation.Nullable android.util.LongSparseArray<android.app.NoteOpEvent> mRejectEvents\npublic @android.annotation.NonNull android.util.ArraySet<java.lang.Long> collectKeys()\npublic @android.app.UidState int getLastAccessUidState(int)\npublic @android.app.UidState int getLastForegroundAccessUidState(int)\npublic @android.app.UidState int getLastBackgroundAccessUidState(int)\npublic @android.app.UidState int getLastRejectUidState(int)\npublic @android.app.UidState int getLastForegroundRejectUidState(int)\npublic @android.app.UidState int getLastBackgroundRejectUidState(int)\npublic long getAccessTime(int,int)\npublic long getRejectTime(int,int)\npublic long getDuration(int,int)\npublic int getProxyUid(int,int)\npublic @android.annotation.Nullable java.lang.String getProxyPackageName(int,int)\npublic @android.annotation.Nullable java.lang.String getProxyAttributionTag(int,int)\nclass OpAttributionEntry extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)")
|
|
@Deprecated
|
|
private void __metadata() {}
|
|
*/
|
|
|
|
|
|
//@formatter:on
|
|
// End of generated code
|
|
|
|
}
|
|
|
|
/**
|
|
* Last {@link #noteOp} and {@link #startOp} events performed for a single op for all uidModes
|
|
* and opFlags.
|
|
*
|
|
* @hide
|
|
*/
|
|
@Immutable
|
|
@SystemApi
|
|
// @DataClass(genHiddenConstructor = true) codegen verifier is broken
|
|
public static final class OpEntry implements Parcelable {
|
|
/** The code of the op */
|
|
private final @IntRange(from = 0, to = _NUM_OP - 1) int mOp;
|
|
/** The mode of the op */
|
|
private final @Mode int mMode;
|
|
/** The attributed entries by attribution tag */
|
|
private final @NonNull Map<String, AttributedOpEntry> mAttributedOpEntries;
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@code "
|
|
+ "#getOpStr()}")
|
|
public int getOp() {
|
|
return mOp;
|
|
}
|
|
|
|
/**
|
|
* @return This entry's op string name, such as {@link #OPSTR_COARSE_LOCATION}.
|
|
*/
|
|
public @NonNull String getOpStr() {
|
|
return sAppOpInfos[mOp].name;
|
|
}
|
|
|
|
/**
|
|
* @hide
|
|
*
|
|
* @deprecated Use {@link #getLastAccessTime(int)} instead
|
|
*/
|
|
@Deprecated
|
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@code "
|
|
+ "#getLastAccessTime(int)}")
|
|
public long getTime() {
|
|
return getLastAccessTime(OP_FLAGS_ALL);
|
|
}
|
|
|
|
/**
|
|
* Return the last access time.
|
|
*
|
|
* @param flags The op flags
|
|
*
|
|
* @return the last access time (in milliseconds since epoch start (January 1, 1970
|
|
* 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no access
|
|
*
|
|
* @see #getLastAccessForegroundTime(int)
|
|
* @see #getLastAccessBackgroundTime(int)
|
|
* @see #getLastAccessTime(int, int, int)
|
|
* @see AttributedOpEntry#getLastAccessTime(int)
|
|
*/
|
|
public long getLastAccessTime(@OpFlags int flags) {
|
|
return getLastAccessTime(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, flags);
|
|
}
|
|
|
|
/**
|
|
* Return the last foreground access time.
|
|
*
|
|
* @param flags The op flags
|
|
*
|
|
* @return the last access time (in milliseconds since epoch start (January 1, 1970
|
|
* 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no foreground access
|
|
*
|
|
* @see #getLastAccessTime(int)
|
|
* @see #getLastAccessBackgroundTime(int)
|
|
* @see #getLastAccessTime(int, int, int)
|
|
* @see AttributedOpEntry#getLastAccessForegroundTime(int)
|
|
*/
|
|
public long getLastAccessForegroundTime(@OpFlags int flags) {
|
|
return getLastAccessTime(MAX_PRIORITY_UID_STATE, resolveFirstUnrestrictedUidState(mOp),
|
|
flags);
|
|
}
|
|
|
|
/**
|
|
* Return the last background access time.
|
|
*
|
|
* @param flags The op flags
|
|
*
|
|
* @return the last access time (in milliseconds since epoch start (January 1, 1970
|
|
* 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no background access
|
|
*
|
|
* @see #getLastAccessTime(int)
|
|
* @see #getLastAccessForegroundTime(int)
|
|
* @see #getLastAccessTime(int, int, int)
|
|
* @see AttributedOpEntry#getLastAccessBackgroundTime(int)
|
|
*/
|
|
public long getLastAccessBackgroundTime(@OpFlags int flags) {
|
|
return getLastAccessTime(resolveLastRestrictedUidState(mOp), MIN_PRIORITY_UID_STATE,
|
|
flags);
|
|
}
|
|
|
|
/**
|
|
* Return the last access event.
|
|
*
|
|
* @param flags The op flags
|
|
*
|
|
* @return the last access event of {@code null} if there was no access
|
|
*/
|
|
private @Nullable NoteOpEvent getLastAccessEvent(@UidState int fromUidState,
|
|
@UidState int toUidState, @OpFlags int flags) {
|
|
NoteOpEvent lastAccessEvent = null;
|
|
for (AttributedOpEntry attributionEntry : mAttributedOpEntries.values()) {
|
|
NoteOpEvent lastAttributionAccessEvent = attributionEntry.getLastAccessEvent(
|
|
fromUidState, toUidState, flags);
|
|
|
|
if (lastAccessEvent == null || (lastAttributionAccessEvent != null
|
|
&& lastAttributionAccessEvent.getNoteTime()
|
|
> lastAccessEvent.getNoteTime())) {
|
|
lastAccessEvent = lastAttributionAccessEvent;
|
|
}
|
|
}
|
|
|
|
return lastAccessEvent;
|
|
}
|
|
|
|
/**
|
|
* Return the last access time.
|
|
*
|
|
* @param fromUidState the lowest uid state to query
|
|
* @param toUidState the highest uid state to query (inclusive)
|
|
* @param flags The op flags
|
|
*
|
|
* @return the last access time (in milliseconds since epoch start (January 1, 1970
|
|
* 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no access
|
|
*
|
|
* @see #getLastAccessTime(int)
|
|
* @see #getLastAccessForegroundTime(int)
|
|
* @see #getLastAccessBackgroundTime(int)
|
|
* @see AttributedOpEntry#getLastAccessTime(int, int, int)
|
|
*/
|
|
public long getLastAccessTime(@UidState int fromUidState, @UidState int toUidState,
|
|
@OpFlags int flags) {
|
|
NoteOpEvent lastEvent = getLastAccessEvent(fromUidState, toUidState, flags);;
|
|
|
|
if (lastEvent == null) {
|
|
return -1;
|
|
}
|
|
|
|
return lastEvent.getNoteTime();
|
|
}
|
|
|
|
/**
|
|
* @hide
|
|
*
|
|
* @deprecated Use {@link #getLastRejectTime(int)} instead
|
|
*/
|
|
@Deprecated
|
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@code "
|
|
+ "#getLastRejectTime(int)}")
|
|
public long getRejectTime() {
|
|
return getLastRejectTime(OP_FLAGS_ALL);
|
|
}
|
|
|
|
/**
|
|
* Return the last rejection time.
|
|
*
|
|
* @param flags The op flags
|
|
*
|
|
* @return the last rejection time (in milliseconds since epoch start (January 1, 1970
|
|
* 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no rejection
|
|
*
|
|
* @see #getLastRejectForegroundTime(int)
|
|
* @see #getLastRejectBackgroundTime(int)
|
|
* @see #getLastRejectTime(int, int, int)
|
|
* @see AttributedOpEntry#getLastRejectTime(int)
|
|
*/
|
|
public long getLastRejectTime(@OpFlags int flags) {
|
|
return getLastRejectTime(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, flags);
|
|
}
|
|
|
|
/**
|
|
* Return the last foreground rejection time.
|
|
*
|
|
* @param flags The op flags
|
|
*
|
|
* @return the last rejection time (in milliseconds since epoch start (January 1, 1970
|
|
* 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no foreground rejection
|
|
*
|
|
* @see #getLastRejectTime(int)
|
|
* @see #getLastRejectBackgroundTime(int)
|
|
* @see #getLastRejectTime(int, int, int)
|
|
* @see AttributedOpEntry#getLastRejectForegroundTime(int)
|
|
*/
|
|
public long getLastRejectForegroundTime(@OpFlags int flags) {
|
|
return getLastRejectTime(MAX_PRIORITY_UID_STATE, resolveFirstUnrestrictedUidState(mOp),
|
|
flags);
|
|
}
|
|
|
|
/**
|
|
* Return the last background rejection time.
|
|
*
|
|
* @param flags The op flags
|
|
*
|
|
* @return the last rejection time (in milliseconds since epoch start (January 1, 1970
|
|
* 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no background rejection
|
|
*
|
|
* @see #getLastRejectTime(int)
|
|
* @see #getLastRejectForegroundTime(int)
|
|
* @see #getLastRejectTime(int, int, int)
|
|
* @see AttributedOpEntry#getLastRejectBackgroundTime(int)
|
|
*/
|
|
public long getLastRejectBackgroundTime(@OpFlags int flags) {
|
|
return getLastRejectTime(resolveLastRestrictedUidState(mOp), MIN_PRIORITY_UID_STATE,
|
|
flags);
|
|
}
|
|
|
|
/**
|
|
* Return the last rejection event.
|
|
*
|
|
* @param flags The op flags
|
|
*
|
|
* @return the last reject event of {@code null} if there was no rejection
|
|
*/
|
|
private @Nullable NoteOpEvent getLastRejectEvent(@UidState int fromUidState,
|
|
@UidState int toUidState, @OpFlags int flags) {
|
|
NoteOpEvent lastRejectEvent = null;
|
|
for (AttributedOpEntry attributionEntry : mAttributedOpEntries.values()) {
|
|
NoteOpEvent lastAttributionRejectEvent = attributionEntry.getLastRejectEvent(
|
|
fromUidState, toUidState, flags);
|
|
|
|
if (lastRejectEvent == null || (lastAttributionRejectEvent != null
|
|
&& lastAttributionRejectEvent.getNoteTime()
|
|
> lastRejectEvent.getNoteTime())) {
|
|
lastRejectEvent = lastAttributionRejectEvent;
|
|
}
|
|
}
|
|
|
|
return lastRejectEvent;
|
|
}
|
|
|
|
/**
|
|
* Return the last rejection time.
|
|
*
|
|
* @param fromUidState the lowest uid state to query
|
|
* @param toUidState the highest uid state to query (inclusive)
|
|
* @param flags The op flags
|
|
*
|
|
* @return the last rejection time (in milliseconds since epoch start (January 1, 1970
|
|
* 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no rejection
|
|
*
|
|
* @see #getLastRejectTime(int)
|
|
* @see #getLastRejectForegroundTime(int)
|
|
* @see #getLastRejectBackgroundTime(int)
|
|
* @see #getLastRejectTime(int, int, int)
|
|
* @see AttributedOpEntry#getLastRejectTime(int, int, int)
|
|
*/
|
|
public long getLastRejectTime(@UidState int fromUidState, @UidState int toUidState,
|
|
@OpFlags int flags) {
|
|
NoteOpEvent lastEvent = getLastRejectEvent(fromUidState, toUidState, flags);
|
|
if (lastEvent == null) {
|
|
return -1;
|
|
}
|
|
|
|
return lastEvent.getNoteTime();
|
|
}
|
|
|
|
/**
|
|
* @return Whether the operation is running.
|
|
*/
|
|
public boolean isRunning() {
|
|
for (AttributedOpEntry opAttributionEntry : mAttributedOpEntries.values()) {
|
|
if (opAttributionEntry.isRunning()) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link #getLastDuration(int)} instead
|
|
*/
|
|
@Deprecated
|
|
public long getDuration() {
|
|
return getLastDuration(OP_FLAGS_ALL);
|
|
}
|
|
|
|
/**
|
|
* Return the duration in milliseconds of the last the access.
|
|
*
|
|
* @param flags The op flags
|
|
*
|
|
* @return the duration in milliseconds or {@code -1} if there was no access
|
|
*
|
|
* @see #getLastForegroundDuration(int)
|
|
* @see #getLastBackgroundDuration(int)
|
|
* @see #getLastDuration(int, int, int)
|
|
* @see AttributedOpEntry#getLastDuration(int)
|
|
*/
|
|
public long getLastDuration(@OpFlags int flags) {
|
|
return getLastDuration(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, flags);
|
|
}
|
|
|
|
/**
|
|
* Return the duration in milliseconds of the last foreground access.
|
|
*
|
|
* @param flags The op flags
|
|
*
|
|
* @return the duration in milliseconds or {@code -1} if there was no foreground access
|
|
*
|
|
* @see #getLastDuration(int)
|
|
* @see #getLastBackgroundDuration(int)
|
|
* @see #getLastDuration(int, int, int)
|
|
* @see AttributedOpEntry#getLastForegroundDuration(int)
|
|
*/
|
|
public long getLastForegroundDuration(@OpFlags int flags) {
|
|
return getLastDuration(MAX_PRIORITY_UID_STATE, resolveFirstUnrestrictedUidState(mOp),
|
|
flags);
|
|
}
|
|
|
|
/**
|
|
* Return the duration in milliseconds of the last background access.
|
|
*
|
|
* @param flags The op flags
|
|
*
|
|
* @return the duration in milliseconds or {@code -1} if there was no background access
|
|
*
|
|
* @see #getLastDuration(int)
|
|
* @see #getLastForegroundDuration(int)
|
|
* @see #getLastDuration(int, int, int)
|
|
* @see AttributedOpEntry#getLastBackgroundDuration(int)
|
|
*/
|
|
public long getLastBackgroundDuration(@OpFlags int flags) {
|
|
return getLastDuration(resolveLastRestrictedUidState(mOp), MIN_PRIORITY_UID_STATE,
|
|
flags);
|
|
}
|
|
|
|
/**
|
|
* Return the duration in milliseconds of the last access.
|
|
*
|
|
* @param fromUidState The lowest UID state for which to query
|
|
* @param toUidState The highest UID state for which to query (inclusive)
|
|
* @param flags The op flags
|
|
*
|
|
* @return the duration in milliseconds or {@code -1} if there was no access
|
|
*
|
|
* @see #getLastDuration(int)
|
|
* @see #getLastForegroundDuration(int)
|
|
* @see #getLastBackgroundDuration(int)
|
|
* @see AttributedOpEntry#getLastDuration(int, int, int)
|
|
*/
|
|
public long getLastDuration(@UidState int fromUidState, @UidState int toUidState,
|
|
@OpFlags int flags) {
|
|
NoteOpEvent lastEvent = getLastAccessEvent(fromUidState, toUidState, flags);
|
|
if (lastEvent == null) {
|
|
return -1;
|
|
}
|
|
|
|
return lastEvent.getDuration();
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link #getLastProxyInfo(int)} instead
|
|
*/
|
|
@Deprecated
|
|
public int getProxyUid() {
|
|
OpEventProxyInfo proxy = getLastProxyInfo(OP_FLAGS_ALL);
|
|
if (proxy == null) {
|
|
return Process.INVALID_UID;
|
|
}
|
|
|
|
return proxy.getUid();
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link #getLastProxyInfo(int)} instead
|
|
*/
|
|
@Deprecated
|
|
public int getProxyUid(@UidState int uidState, @OpFlags int flags) {
|
|
OpEventProxyInfo proxy = getLastProxyInfo(uidState, uidState, flags);
|
|
if (proxy == null) {
|
|
return Process.INVALID_UID;
|
|
}
|
|
|
|
return proxy.getUid();
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link #getLastProxyInfo(int)} instead
|
|
*/
|
|
@Deprecated
|
|
public @Nullable String getProxyPackageName() {
|
|
OpEventProxyInfo proxy = getLastProxyInfo(OP_FLAGS_ALL);
|
|
if (proxy == null) {
|
|
return null;
|
|
}
|
|
|
|
return proxy.getPackageName();
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link #getLastProxyInfo(int)} instead
|
|
*/
|
|
@Deprecated
|
|
public @Nullable String getProxyPackageName(@UidState int uidState, @OpFlags int flags) {
|
|
OpEventProxyInfo proxy = getLastProxyInfo(uidState, uidState, flags);
|
|
if (proxy == null) {
|
|
return null;
|
|
}
|
|
|
|
return proxy.getPackageName();
|
|
}
|
|
|
|
/**
|
|
* Gets the proxy info of the app that performed the last access on behalf of this app and
|
|
* as a result blamed the op on this app.
|
|
*
|
|
* @param flags The op flags
|
|
*
|
|
* @return The proxy info or {@code null} if there was no proxy access
|
|
*
|
|
* @see #getLastForegroundProxyInfo(int)
|
|
* @see #getLastBackgroundProxyInfo(int)
|
|
* @see #getLastProxyInfo(int, int, int)
|
|
* @see AttributedOpEntry#getLastProxyInfo(int)
|
|
*/
|
|
public @Nullable OpEventProxyInfo getLastProxyInfo(@OpFlags int flags) {
|
|
return getLastProxyInfo(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, flags);
|
|
}
|
|
|
|
/**
|
|
* Gets the proxy info of the app that performed the last foreground access on behalf of
|
|
* this app and as a result blamed the op on this app.
|
|
*
|
|
* @param flags The op flags
|
|
*
|
|
* @return The proxy info or {@code null} if there was no foreground proxy access
|
|
*
|
|
* @see #getLastProxyInfo(int)
|
|
* @see #getLastBackgroundProxyInfo(int)
|
|
* @see #getLastProxyInfo(int, int, int)
|
|
* @see AttributedOpEntry#getLastForegroundProxyInfo(int)
|
|
*/
|
|
public @Nullable OpEventProxyInfo getLastForegroundProxyInfo(@OpFlags int flags) {
|
|
return getLastProxyInfo(MAX_PRIORITY_UID_STATE, resolveFirstUnrestrictedUidState(mOp),
|
|
flags);
|
|
}
|
|
|
|
/**
|
|
* Gets the proxy info of the app that performed the last background access on behalf of
|
|
* this app and as a result blamed the op on this app.
|
|
*
|
|
* @param flags The op flags
|
|
*
|
|
* @return The proxy info or {@code null} if there was no background proxy access
|
|
*
|
|
* @see #getLastProxyInfo(int)
|
|
* @see #getLastForegroundProxyInfo(int)
|
|
* @see #getLastProxyInfo(int, int, int)
|
|
* @see AttributedOpEntry#getLastBackgroundProxyInfo(int)
|
|
*/
|
|
public @Nullable OpEventProxyInfo getLastBackgroundProxyInfo(@OpFlags int flags) {
|
|
return getLastProxyInfo(resolveLastRestrictedUidState(mOp), MIN_PRIORITY_UID_STATE,
|
|
flags);
|
|
}
|
|
|
|
/**
|
|
* Gets the proxy info of the app that performed the last access on behalf of this app and
|
|
* as a result blamed the op on this app.
|
|
*
|
|
* @param fromUidState The lowest UID state for which to query
|
|
* @param toUidState The highest UID state for which to query (inclusive)
|
|
* @param flags The op flags
|
|
*
|
|
* @return The proxy info or {@code null} if there was no proxy access
|
|
*
|
|
* @see #getLastProxyInfo(int)
|
|
* @see #getLastForegroundProxyInfo(int)
|
|
* @see #getLastBackgroundProxyInfo(int)
|
|
* @see AttributedOpEntry#getLastProxyInfo(int, int, int)
|
|
*/
|
|
public @Nullable OpEventProxyInfo getLastProxyInfo(@UidState int fromUidState,
|
|
@UidState int toUidState, @OpFlags int flags) {
|
|
NoteOpEvent lastEvent = getLastAccessEvent(fromUidState, toUidState, flags);
|
|
if (lastEvent == null) {
|
|
return null;
|
|
}
|
|
|
|
return lastEvent.getProxy();
|
|
}
|
|
|
|
|
|
|
|
// Code below generated by codegen v1.0.14.
|
|
//
|
|
// DO NOT MODIFY!
|
|
// CHECKSTYLE:OFF Generated code
|
|
//
|
|
// To regenerate run:
|
|
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/app/AppOpsManager.java
|
|
//
|
|
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
|
|
// Settings > Editor > Code Style > Formatter Control
|
|
//@formatter:off
|
|
|
|
|
|
/**
|
|
* Creates a new OpEntry.
|
|
*
|
|
* @param op
|
|
* The code of the op
|
|
* @param mode
|
|
* The mode of the op
|
|
* @param attributedOpEntries
|
|
* The attributions that have been used when noting the op
|
|
* @hide
|
|
*/
|
|
@DataClass.Generated.Member
|
|
public OpEntry(
|
|
@IntRange(from = 0, to = _NUM_OP - 1) int op,
|
|
@Mode int mode,
|
|
@NonNull Map<String, AttributedOpEntry> attributedOpEntries) {
|
|
this.mOp = op;
|
|
com.android.internal.util.AnnotationValidations.validate(
|
|
IntRange.class, null, mOp,
|
|
"from", 0,
|
|
"to", _NUM_OP - 1);
|
|
this.mMode = mode;
|
|
com.android.internal.util.AnnotationValidations.validate(
|
|
Mode.class, null, mMode);
|
|
this.mAttributedOpEntries = attributedOpEntries;
|
|
com.android.internal.util.AnnotationValidations.validate(
|
|
NonNull.class, null, mAttributedOpEntries);
|
|
|
|
// onConstructed(); // You can define this method to get a callback
|
|
}
|
|
|
|
/**
|
|
* The mode of the op
|
|
*/
|
|
@DataClass.Generated.Member
|
|
public @Mode int getMode() {
|
|
return mMode;
|
|
}
|
|
|
|
/**
|
|
* The attributed entries keyed by attribution tag.
|
|
*
|
|
* @see Context#createAttributionContext(String)
|
|
* @see #noteOp(String, int, String, String, String)
|
|
*/
|
|
@DataClass.Generated.Member
|
|
public @NonNull Map<String, AttributedOpEntry> getAttributedOpEntries() {
|
|
return mAttributedOpEntries;
|
|
}
|
|
|
|
@Override
|
|
@DataClass.Generated.Member
|
|
public void writeToParcel(Parcel dest, int flags) {
|
|
// You can override field parcelling by defining methods like:
|
|
// void parcelFieldName(Parcel dest, int flags) { ... }
|
|
|
|
dest.writeInt(mOp);
|
|
dest.writeInt(mMode);
|
|
dest.writeMap(mAttributedOpEntries);
|
|
}
|
|
|
|
@Override
|
|
@DataClass.Generated.Member
|
|
public int describeContents() { return 0; }
|
|
|
|
/** @hide */
|
|
@SuppressWarnings({"unchecked", "RedundantCast"})
|
|
@DataClass.Generated.Member
|
|
/* package-private */ OpEntry(@NonNull Parcel in) {
|
|
// You can override field unparcelling by defining methods like:
|
|
// static FieldType unparcelFieldName(Parcel in) { ... }
|
|
|
|
int op = in.readInt();
|
|
int mode = in.readInt();
|
|
Map<String, AttributedOpEntry> attributions = new java.util.LinkedHashMap<>();
|
|
in.readMap(attributions, AttributedOpEntry.class.getClassLoader());
|
|
|
|
this.mOp = op;
|
|
com.android.internal.util.AnnotationValidations.validate(
|
|
IntRange.class, null, mOp,
|
|
"from", 0,
|
|
"to", _NUM_OP - 1);
|
|
this.mMode = mode;
|
|
com.android.internal.util.AnnotationValidations.validate(
|
|
Mode.class, null, mMode);
|
|
this.mAttributedOpEntries = attributions;
|
|
com.android.internal.util.AnnotationValidations.validate(
|
|
NonNull.class, null, mAttributedOpEntries);
|
|
|
|
// onConstructed(); // You can define this method to get a callback
|
|
}
|
|
|
|
@DataClass.Generated.Member
|
|
public static final @NonNull Parcelable.Creator<OpEntry> CREATOR
|
|
= new Parcelable.Creator<OpEntry>() {
|
|
@Override
|
|
public OpEntry[] newArray(int size) {
|
|
return new OpEntry[size];
|
|
}
|
|
|
|
@Override
|
|
public OpEntry createFromParcel(@NonNull Parcel in) {
|
|
return new OpEntry(in);
|
|
}
|
|
};
|
|
|
|
/*
|
|
@DataClass.Generated(
|
|
time = 1574809856259L,
|
|
codegenVersion = "1.0.14",
|
|
sourceFile = "frameworks/base/core/java/android/app/AppOpsManager.java",
|
|
inputSignatures = "private final @android.annotation.IntRange(from=0L, to=_NUM_OP - 1) int mOp\nprivate final @android.app.Mode int mMode\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,android.app.OpAttributionEntry> mAttributions\npublic @android.annotation.UnsupportedAppUsage(maxTargetSdk=Build.VERSION_CODES.Q, publicAlternatives=\"{@code \" + \"#getOpStr()}\") int getOp()\npublic @android.annotation.NonNull java.lang.String getOpStr()\npublic @java.lang.Deprecated @android.annotation.UnsupportedAppUsage(maxTargetSdk=Build.VERSION_CODES.Q, publicAlternatives=\"{@code \" + \"#getAccessTime(int, int)}\") long getTime()\npublic @java.lang.Deprecated long getLastAccessTime(int)\npublic @java.lang.Deprecated long getLastAccessForegroundTime(int)\npublic @java.lang.Deprecated long getLastAccessBackgroundTime(int)\npublic @java.lang.Deprecated long getLastAccessTime(int,int,int)\npublic @java.lang.Deprecated @android.annotation.UnsupportedAppUsage(maxTargetSdk=Build.VERSION_CODES.Q, publicAlternatives=\"{@code \" + \"#getLastRejectTime(int, int, int)}\") long getRejectTime()\npublic @java.lang.Deprecated long getLastRejectTime(int)\npublic @java.lang.Deprecated long getLastRejectForegroundTime(int)\npublic @java.lang.Deprecated long getLastRejectBackgroundTime(int)\npublic @java.lang.Deprecated long getLastRejectTime(int,int,int)\npublic long getAccessTime(int,int)\npublic long getRejectTime(int,int)\npublic boolean isRunning()\nprivate android.app.NoteOpEvent getLastAccessEvent(int,int,int)\npublic @java.lang.Deprecated long getDuration()\npublic @java.lang.Deprecated long getLastForegroundDuration(int)\npublic @java.lang.Deprecated long getLastBackgroundDuration(int)\npublic @java.lang.Deprecated long getLastDuration(int,int,int)\npublic @java.lang.Deprecated int getProxyUid()\npublic @java.lang.Deprecated @android.annotation.Nullable java.lang.String getProxyPackageName()\nprivate @android.app.UidState int getLastAccessUidStateForFlagsInStatesOfAllAttributions(int,int,int)\npublic @android.app.UidState int getLastAccessUidState(int)\npublic @android.app.UidState int getLastForegroundAccessUidState(int)\npublic @android.app.UidState int getLastBackgroundAccessUidState(int)\nprivate @android.app.UidState int getLastRejectUidStateForFlagsInStatesOfAllAttributions(int,int,int)\npublic @android.app.UidState int getLastRejectUidState(int)\npublic @android.app.UidState int getLastForegroundRejectUidState(int)\npublic @android.app.UidState int getLastBackgroundRejectUidState(int)\npublic long getDuration(int,int)\npublic int getProxyUid(int,int)\nprivate int getProxyUid(int,int,int)\npublic @android.annotation.Nullable java.lang.String getProxyPackageName(int,int)\nprivate @android.annotation.Nullable java.lang.String getProxyPackageName(int,int,int)\nclass OpEntry extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)")
|
|
@Deprecated
|
|
private void __metadata() {}
|
|
*/
|
|
|
|
|
|
//@formatter:on
|
|
// End of generated code
|
|
|
|
}
|
|
|
|
/** @hide */
|
|
public interface HistoricalOpsVisitor {
|
|
void visitHistoricalOps(@NonNull HistoricalOps ops);
|
|
void visitHistoricalUidOps(@NonNull HistoricalUidOps ops);
|
|
void visitHistoricalPackageOps(@NonNull HistoricalPackageOps ops);
|
|
void visitHistoricalAttributionOps(@NonNull AttributedHistoricalOps ops);
|
|
void visitHistoricalOp(@NonNull HistoricalOp ops);
|
|
}
|
|
|
|
/**
|
|
* Flag for querying app op history: get only aggregate information (counts of events) and no
|
|
* discret accesses information - specific accesses with timestamp.
|
|
*
|
|
* @see #getHistoricalOps(HistoricalOpsRequest, Executor, Consumer)
|
|
*
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
@SystemApi
|
|
public static final int HISTORY_FLAG_AGGREGATE = 1 << 0;
|
|
|
|
/**
|
|
* Flag for querying app op history: get only discrete access information (only specific
|
|
* accesses with timestamps) and no aggregate information (counts over time).
|
|
*
|
|
* @see #getHistoricalOps(HistoricalOpsRequest, Executor, Consumer)
|
|
*
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
@SystemApi
|
|
public static final int HISTORY_FLAG_DISCRETE = 1 << 1;
|
|
|
|
/**
|
|
* Flag for querying app op history: assemble attribution chains, and attach the last visible
|
|
* node in the chain to the start as a proxy info. This only applies to discrete accesses.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public static final int HISTORY_FLAG_GET_ATTRIBUTION_CHAINS = 1 << 2;
|
|
|
|
/**
|
|
* Flag for querying app op history: get all types of historical access information.
|
|
*
|
|
* @see #getHistoricalOps(HistoricalOpsRequest, Executor, Consumer)
|
|
*
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
@SystemApi
|
|
public static final int HISTORY_FLAGS_ALL = HISTORY_FLAG_AGGREGATE
|
|
| HISTORY_FLAG_DISCRETE;
|
|
|
|
/** @hide */
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@IntDef(flag = true, prefix = { "HISTORY_FLAG_" }, value = {
|
|
HISTORY_FLAG_AGGREGATE,
|
|
HISTORY_FLAG_DISCRETE,
|
|
HISTORY_FLAG_GET_ATTRIBUTION_CHAINS
|
|
})
|
|
public @interface OpHistoryFlags {}
|
|
|
|
/**
|
|
* Specifies what parameters to filter historical appop requests for
|
|
*
|
|
* @hide
|
|
*/
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@IntDef(flag = true, prefix = { "FILTER_BY_" }, value = {
|
|
FILTER_BY_UID,
|
|
FILTER_BY_PACKAGE_NAME,
|
|
FILTER_BY_ATTRIBUTION_TAG,
|
|
FILTER_BY_OP_NAMES
|
|
})
|
|
public @interface HistoricalOpsRequestFilter {}
|
|
|
|
/**
|
|
* Filter historical appop request by uid.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int FILTER_BY_UID = 1<<0;
|
|
|
|
/**
|
|
* Filter historical appop request by package name.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int FILTER_BY_PACKAGE_NAME = 1<<1;
|
|
|
|
/**
|
|
* Filter historical appop request by attribution tag.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int FILTER_BY_ATTRIBUTION_TAG = 1<<2;
|
|
|
|
/**
|
|
* Filter historical appop request by op names.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int FILTER_BY_OP_NAMES = 1<<3;
|
|
|
|
/**
|
|
* Request for getting historical app op usage. The request acts
|
|
* as a filtering criteria when querying historical op usage.
|
|
*
|
|
* @hide
|
|
*/
|
|
@Immutable
|
|
@SystemApi
|
|
public static final class HistoricalOpsRequest {
|
|
private final int mUid;
|
|
private final @Nullable String mPackageName;
|
|
private final @Nullable String mAttributionTag;
|
|
private final @Nullable List<String> mOpNames;
|
|
private final @OpHistoryFlags int mHistoryFlags;
|
|
private final @HistoricalOpsRequestFilter int mFilter;
|
|
private final long mBeginTimeMillis;
|
|
private final long mEndTimeMillis;
|
|
private final @OpFlags int mFlags;
|
|
|
|
private HistoricalOpsRequest(int uid, @Nullable String packageName,
|
|
@Nullable String attributionTag, @Nullable List<String> opNames,
|
|
@OpHistoryFlags int historyFlags, @HistoricalOpsRequestFilter int filter,
|
|
long beginTimeMillis, long endTimeMillis, @OpFlags int flags) {
|
|
mUid = uid;
|
|
mPackageName = packageName;
|
|
mAttributionTag = attributionTag;
|
|
mOpNames = opNames;
|
|
mHistoryFlags = historyFlags;
|
|
mFilter = filter;
|
|
mBeginTimeMillis = beginTimeMillis;
|
|
mEndTimeMillis = endTimeMillis;
|
|
mFlags = flags;
|
|
}
|
|
|
|
/**
|
|
* Builder for creating a {@link HistoricalOpsRequest}.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public static final class Builder {
|
|
private int mUid = Process.INVALID_UID;
|
|
private @Nullable String mPackageName;
|
|
private @Nullable String mAttributionTag;
|
|
private @Nullable List<String> mOpNames;
|
|
private @OpHistoryFlags int mHistoryFlags;
|
|
private @HistoricalOpsRequestFilter int mFilter;
|
|
private final long mBeginTimeMillis;
|
|
private final long mEndTimeMillis;
|
|
private @OpFlags int mFlags = OP_FLAGS_ALL;
|
|
|
|
/**
|
|
* Creates a new builder.
|
|
*
|
|
* @param beginTimeMillis The beginning of the interval in milliseconds since
|
|
* epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). Must be non
|
|
* negative.
|
|
* @param endTimeMillis The end of the interval in milliseconds since
|
|
* epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). Must be after
|
|
* {@code beginTimeMillis}. Pass {@link Long#MAX_VALUE} to get the most recent
|
|
* history including ops that happen while this call is in flight.
|
|
*/
|
|
public Builder(long beginTimeMillis, long endTimeMillis) {
|
|
Preconditions.checkArgument(beginTimeMillis >= 0 && beginTimeMillis < endTimeMillis,
|
|
"beginTimeMillis must be non negative and lesser than endTimeMillis");
|
|
mBeginTimeMillis = beginTimeMillis;
|
|
mEndTimeMillis = endTimeMillis;
|
|
mHistoryFlags = HISTORY_FLAG_AGGREGATE;
|
|
}
|
|
|
|
/**
|
|
* Sets the UID to query for.
|
|
*
|
|
* @param uid The uid. Pass {@link android.os.Process#INVALID_UID} for any uid.
|
|
* @return This builder.
|
|
*/
|
|
public @NonNull Builder setUid(int uid) {
|
|
Preconditions.checkArgument(uid == Process.INVALID_UID || uid >= 0,
|
|
"uid must be " + Process.INVALID_UID + " or non negative");
|
|
mUid = uid;
|
|
|
|
if (uid == Process.INVALID_UID) {
|
|
mFilter &= ~FILTER_BY_UID;
|
|
} else {
|
|
mFilter |= FILTER_BY_UID;
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the package to query for.
|
|
*
|
|
* @param packageName The package name. <code>Null</code> for any package.
|
|
* @return This builder.
|
|
*/
|
|
public @NonNull Builder setPackageName(@Nullable String packageName) {
|
|
mPackageName = packageName;
|
|
|
|
if (packageName == null) {
|
|
mFilter &= ~FILTER_BY_PACKAGE_NAME;
|
|
} else {
|
|
mFilter |= FILTER_BY_PACKAGE_NAME;
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the attribution tag to query for.
|
|
*
|
|
* @param attributionTag attribution tag
|
|
* @return This builder.
|
|
*/
|
|
public @NonNull Builder setAttributionTag(@Nullable String attributionTag) {
|
|
mAttributionTag = attributionTag;
|
|
mFilter |= FILTER_BY_ATTRIBUTION_TAG;
|
|
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the op names to query for.
|
|
*
|
|
* @param opNames The op names. <code>Null</code> for any op.
|
|
* @return This builder.
|
|
*/
|
|
public @NonNull Builder setOpNames(@Nullable List<String> opNames) {
|
|
if (opNames != null) {
|
|
final int opCount = opNames.size();
|
|
for (int i = 0; i < opCount; i++) {
|
|
Preconditions.checkArgument(AppOpsManager.strOpToOp(
|
|
opNames.get(i)) != AppOpsManager.OP_NONE);
|
|
}
|
|
}
|
|
mOpNames = opNames;
|
|
|
|
if (mOpNames == null) {
|
|
mFilter &= ~FILTER_BY_OP_NAMES;
|
|
} else {
|
|
mFilter |= FILTER_BY_OP_NAMES;
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the op flags to query for. The flags specify the type of
|
|
* op data being queried.
|
|
*
|
|
* @param flags The flags which are any combination of
|
|
* {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
|
|
* {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
|
|
* {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
|
|
* for any flag.
|
|
* @return This builder.
|
|
*/
|
|
public @NonNull Builder setFlags(@OpFlags int flags) {
|
|
Preconditions.checkFlagsArgument(flags, OP_FLAGS_ALL);
|
|
mFlags = flags;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Specifies what type of historical information to query.
|
|
*
|
|
* @param flags Flags for the historical types to fetch which are any
|
|
* combination of {@link #HISTORY_FLAG_AGGREGATE}, {@link #HISTORY_FLAG_DISCRETE},
|
|
* {@link #HISTORY_FLAGS_ALL}. The default is {@link #HISTORY_FLAG_AGGREGATE}.
|
|
* @return This builder.
|
|
*/
|
|
public @NonNull Builder setHistoryFlags(@OpHistoryFlags int flags) {
|
|
Preconditions.checkFlagsArgument(flags,
|
|
HISTORY_FLAGS_ALL | HISTORY_FLAG_GET_ATTRIBUTION_CHAINS);
|
|
mHistoryFlags = flags;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* @return a new {@link HistoricalOpsRequest}.
|
|
*/
|
|
public @NonNull HistoricalOpsRequest build() {
|
|
return new HistoricalOpsRequest(mUid, mPackageName, mAttributionTag, mOpNames,
|
|
mHistoryFlags, mFilter, mBeginTimeMillis, mEndTimeMillis, mFlags);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This class represents historical app op state of all UIDs for a given time interval.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public static final class HistoricalOps implements Parcelable {
|
|
private long mBeginTimeMillis;
|
|
private long mEndTimeMillis;
|
|
private @Nullable SparseArray<HistoricalUidOps> mHistoricalUidOps;
|
|
|
|
/** @hide */
|
|
@TestApi
|
|
public HistoricalOps(long beginTimeMillis, long endTimeMillis) {
|
|
Preconditions.checkState(beginTimeMillis <= endTimeMillis);
|
|
mBeginTimeMillis = beginTimeMillis;
|
|
mEndTimeMillis = endTimeMillis;
|
|
}
|
|
|
|
/** @hide */
|
|
public HistoricalOps(@NonNull HistoricalOps other) {
|
|
mBeginTimeMillis = other.mBeginTimeMillis;
|
|
mEndTimeMillis = other.mEndTimeMillis;
|
|
Preconditions.checkState(mBeginTimeMillis <= mEndTimeMillis);
|
|
if (other.mHistoricalUidOps != null) {
|
|
final int opCount = other.getUidCount();
|
|
for (int i = 0; i < opCount; i++) {
|
|
final HistoricalUidOps origOps = other.getUidOpsAt(i);
|
|
final HistoricalUidOps clonedOps = new HistoricalUidOps(origOps);
|
|
if (mHistoricalUidOps == null) {
|
|
mHistoricalUidOps = new SparseArray<>(opCount);
|
|
}
|
|
mHistoricalUidOps.put(clonedOps.getUid(), clonedOps);
|
|
}
|
|
}
|
|
}
|
|
|
|
private HistoricalOps(Parcel parcel) {
|
|
mBeginTimeMillis = parcel.readLong();
|
|
mEndTimeMillis = parcel.readLong();
|
|
final int[] uids = parcel.createIntArray();
|
|
if (!ArrayUtils.isEmpty(uids)) {
|
|
final ParceledListSlice<HistoricalUidOps> listSlice = parcel.readParcelable(
|
|
HistoricalOps.class.getClassLoader(), android.content.pm.ParceledListSlice.class);
|
|
final List<HistoricalUidOps> uidOps = (listSlice != null)
|
|
? listSlice.getList() : null;
|
|
if (uidOps == null) {
|
|
return;
|
|
}
|
|
for (int i = 0; i < uids.length; i++) {
|
|
if (mHistoricalUidOps == null) {
|
|
mHistoricalUidOps = new SparseArray<>();
|
|
}
|
|
mHistoricalUidOps.put(uids[i], uidOps.get(i));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Splice a piece from the beginning of these ops.
|
|
*
|
|
* @param splicePoint The fraction of the data to be spliced off.
|
|
*
|
|
* @hide
|
|
*/
|
|
public @NonNull HistoricalOps spliceFromBeginning(double splicePoint) {
|
|
return splice(splicePoint, true);
|
|
}
|
|
|
|
/**
|
|
* Splice a piece from the end of these ops.
|
|
*
|
|
* @param fractionToRemove The fraction of the data to be spliced off.
|
|
*
|
|
* @hide
|
|
*/
|
|
public @NonNull HistoricalOps spliceFromEnd(double fractionToRemove) {
|
|
return splice(fractionToRemove, false);
|
|
}
|
|
|
|
/**
|
|
* Splice a piece from the beginning or end of these ops.
|
|
*
|
|
* @param fractionToRemove The fraction of the data to be spliced off.
|
|
* @param beginning Whether to splice off the beginning or the end.
|
|
*
|
|
* @return The spliced off part.
|
|
*
|
|
* @hide
|
|
*/
|
|
private @Nullable HistoricalOps splice(double fractionToRemove, boolean beginning) {
|
|
final long spliceBeginTimeMills;
|
|
final long spliceEndTimeMills;
|
|
if (beginning) {
|
|
spliceBeginTimeMills = mBeginTimeMillis;
|
|
spliceEndTimeMills = (long) (mBeginTimeMillis
|
|
+ getDurationMillis() * fractionToRemove);
|
|
mBeginTimeMillis = spliceEndTimeMills;
|
|
} else {
|
|
spliceBeginTimeMills = (long) (mEndTimeMillis
|
|
- getDurationMillis() * fractionToRemove);
|
|
spliceEndTimeMills = mEndTimeMillis;
|
|
mEndTimeMillis = spliceBeginTimeMills;
|
|
}
|
|
|
|
HistoricalOps splice = null;
|
|
final int uidCount = getUidCount();
|
|
for (int i = 0; i < uidCount; i++) {
|
|
final HistoricalUidOps origOps = getUidOpsAt(i);
|
|
final HistoricalUidOps spliceOps = origOps.splice(fractionToRemove);
|
|
if (spliceOps != null) {
|
|
if (splice == null) {
|
|
splice = new HistoricalOps(spliceBeginTimeMills, spliceEndTimeMills);
|
|
}
|
|
if (splice.mHistoricalUidOps == null) {
|
|
splice.mHistoricalUidOps = new SparseArray<>();
|
|
}
|
|
splice.mHistoricalUidOps.put(spliceOps.getUid(), spliceOps);
|
|
}
|
|
}
|
|
return splice;
|
|
}
|
|
|
|
/**
|
|
* Merge the passed ops into the current ones. The time interval is a
|
|
* union of the current and passed in one and the passed in data is
|
|
* folded into the data of this instance.
|
|
*
|
|
* @hide
|
|
*/
|
|
public void merge(@NonNull HistoricalOps other) {
|
|
mBeginTimeMillis = Math.min(mBeginTimeMillis, other.mBeginTimeMillis);
|
|
mEndTimeMillis = Math.max(mEndTimeMillis, other.mEndTimeMillis);
|
|
final int uidCount = other.getUidCount();
|
|
for (int i = 0; i < uidCount; i++) {
|
|
final HistoricalUidOps otherUidOps = other.getUidOpsAt(i);
|
|
final HistoricalUidOps thisUidOps = getUidOps(otherUidOps.getUid());
|
|
if (thisUidOps != null) {
|
|
thisUidOps.merge(otherUidOps);
|
|
} else {
|
|
if (mHistoricalUidOps == null) {
|
|
mHistoricalUidOps = new SparseArray<>();
|
|
}
|
|
mHistoricalUidOps.put(otherUidOps.getUid(), otherUidOps);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* AppPermissionUsage the ops to leave only the data we filter for.
|
|
*
|
|
* @param uid Uid to filter for.
|
|
* @param packageName Package to filter for.
|
|
* @param attributionTag attribution tag to filter for
|
|
* @param opNames Ops to filter for.
|
|
* @param filter Which parameters to filter on.
|
|
* @param beginTimeMillis The begin time to filter for or {@link Long#MIN_VALUE} for all.
|
|
* @param endTimeMillis The end time to filter for or {@link Long#MAX_VALUE} for all.
|
|
*
|
|
* @hide
|
|
*/
|
|
public void filter(int uid, @Nullable String packageName, @Nullable String attributionTag,
|
|
@Nullable String[] opNames, @OpHistoryFlags int historyFilter,
|
|
@HistoricalOpsRequestFilter int filter,
|
|
long beginTimeMillis, long endTimeMillis) {
|
|
final long durationMillis = getDurationMillis();
|
|
mBeginTimeMillis = Math.max(mBeginTimeMillis, beginTimeMillis);
|
|
mEndTimeMillis = Math.min(mEndTimeMillis, endTimeMillis);
|
|
final double scaleFactor = Math.min((double) (endTimeMillis - beginTimeMillis)
|
|
/ (double) durationMillis, 1);
|
|
final int uidCount = getUidCount();
|
|
for (int i = uidCount - 1; i >= 0; i--) {
|
|
final HistoricalUidOps uidOp = mHistoricalUidOps.valueAt(i);
|
|
if ((filter & FILTER_BY_UID) != 0 && uid != uidOp.getUid()) {
|
|
mHistoricalUidOps.removeAt(i);
|
|
} else {
|
|
uidOp.filter(packageName, attributionTag, opNames, filter, historyFilter,
|
|
scaleFactor, mBeginTimeMillis, mEndTimeMillis);
|
|
if (uidOp.getPackageCount() == 0) {
|
|
mHistoricalUidOps.removeAt(i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @hide */
|
|
public boolean isEmpty() {
|
|
if (getBeginTimeMillis() >= getEndTimeMillis()) {
|
|
return true;
|
|
}
|
|
final int uidCount = getUidCount();
|
|
for (int i = uidCount - 1; i >= 0; i--) {
|
|
final HistoricalUidOps uidOp = mHistoricalUidOps.valueAt(i);
|
|
if (!uidOp.isEmpty()) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/** @hide */
|
|
public long getDurationMillis() {
|
|
return mEndTimeMillis - mBeginTimeMillis;
|
|
}
|
|
|
|
/** @hide */
|
|
@TestApi
|
|
public void increaseAccessCount(int opCode, int uid, @NonNull String packageName,
|
|
@Nullable String attributionTag, @UidState int uidState, @OpFlags int flags,
|
|
long increment) {
|
|
getOrCreateHistoricalUidOps(uid).increaseAccessCount(opCode,
|
|
packageName, attributionTag, uidState, flags, increment);
|
|
}
|
|
|
|
/** @hide */
|
|
@TestApi
|
|
public void increaseRejectCount(int opCode, int uid, @NonNull String packageName,
|
|
@Nullable String attributionTag, @UidState int uidState, @OpFlags int flags,
|
|
long increment) {
|
|
getOrCreateHistoricalUidOps(uid).increaseRejectCount(opCode,
|
|
packageName, attributionTag, uidState, flags, increment);
|
|
}
|
|
|
|
/** @hide */
|
|
@TestApi
|
|
public void increaseAccessDuration(int opCode, int uid, @NonNull String packageName,
|
|
@Nullable String attributionTag, @UidState int uidState, @OpFlags int flags,
|
|
long increment) {
|
|
getOrCreateHistoricalUidOps(uid).increaseAccessDuration(opCode,
|
|
packageName, attributionTag, uidState, flags, increment);
|
|
}
|
|
|
|
/** @hide */
|
|
@TestApi
|
|
public void addDiscreteAccess(int opCode, int uid, @NonNull String packageName,
|
|
@Nullable String attributionTag, @UidState int uidState, @OpFlags int opFlag,
|
|
long discreteAccessTime, long discreteAccessDuration) {
|
|
getOrCreateHistoricalUidOps(uid).addDiscreteAccess(opCode, packageName, attributionTag,
|
|
uidState, opFlag, discreteAccessTime, discreteAccessDuration, null);
|
|
}
|
|
|
|
/** @hide */
|
|
public void addDiscreteAccess(int opCode, int uid, @NonNull String packageName,
|
|
@Nullable String attributionTag, @UidState int uidState, @OpFlags int opFlag,
|
|
long discreteAccessTime, long discreteAccessDuration,
|
|
@Nullable OpEventProxyInfo proxy) {
|
|
getOrCreateHistoricalUidOps(uid).addDiscreteAccess(opCode, packageName, attributionTag,
|
|
uidState, opFlag, discreteAccessTime, discreteAccessDuration, proxy);
|
|
}
|
|
|
|
|
|
/** @hide */
|
|
@TestApi
|
|
public void offsetBeginAndEndTime(long offsetMillis) {
|
|
mBeginTimeMillis += offsetMillis;
|
|
mEndTimeMillis += offsetMillis;
|
|
}
|
|
|
|
/** @hide */
|
|
public void setBeginAndEndTime(long beginTimeMillis, long endTimeMillis) {
|
|
mBeginTimeMillis = beginTimeMillis;
|
|
mEndTimeMillis = endTimeMillis;
|
|
}
|
|
|
|
/** @hide */
|
|
public void setBeginTime(long beginTimeMillis) {
|
|
mBeginTimeMillis = beginTimeMillis;
|
|
}
|
|
|
|
/** @hide */
|
|
public void setEndTime(long endTimeMillis) {
|
|
mEndTimeMillis = endTimeMillis;
|
|
}
|
|
|
|
/**
|
|
* @return The beginning of the interval in milliseconds since
|
|
* epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian).
|
|
*/
|
|
public long getBeginTimeMillis() {
|
|
return mBeginTimeMillis;
|
|
}
|
|
|
|
/**
|
|
* @return The end of the interval in milliseconds since
|
|
* epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian).
|
|
*/
|
|
public long getEndTimeMillis() {
|
|
return mEndTimeMillis;
|
|
}
|
|
|
|
/**
|
|
* Gets number of UIDs with historical ops.
|
|
*
|
|
* @return The number of UIDs with historical ops.
|
|
*
|
|
* @see #getUidOpsAt(int)
|
|
*/
|
|
public @IntRange(from = 0) int getUidCount() {
|
|
if (mHistoricalUidOps == null) {
|
|
return 0;
|
|
}
|
|
return mHistoricalUidOps.size();
|
|
}
|
|
|
|
/**
|
|
* Gets the historical UID ops at a given index.
|
|
*
|
|
* @param index The index.
|
|
*
|
|
* @return The historical UID ops at the given index.
|
|
*
|
|
* @see #getUidCount()
|
|
*/
|
|
public @NonNull HistoricalUidOps getUidOpsAt(@IntRange(from = 0) int index) {
|
|
if (mHistoricalUidOps == null) {
|
|
throw new IndexOutOfBoundsException();
|
|
}
|
|
return mHistoricalUidOps.valueAt(index);
|
|
}
|
|
|
|
/**
|
|
* Gets the historical UID ops for a given UID.
|
|
*
|
|
* @param uid The UID.
|
|
*
|
|
* @return The historical ops for the UID.
|
|
*/
|
|
public @Nullable HistoricalUidOps getUidOps(int uid) {
|
|
if (mHistoricalUidOps == null) {
|
|
return null;
|
|
}
|
|
return mHistoricalUidOps.get(uid);
|
|
}
|
|
|
|
/** @hide */
|
|
public void clearHistory(int uid, @NonNull String packageName) {
|
|
HistoricalUidOps historicalUidOps = getOrCreateHistoricalUidOps(uid);
|
|
historicalUidOps.clearHistory(packageName);
|
|
if (historicalUidOps.isEmpty()) {
|
|
mHistoricalUidOps.remove(uid);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int describeContents() {
|
|
return 0;
|
|
}
|
|
|
|
@Override
|
|
public void writeToParcel(Parcel parcel, int flags) {
|
|
parcel.writeLong(mBeginTimeMillis);
|
|
parcel.writeLong(mEndTimeMillis);
|
|
if (mHistoricalUidOps != null) {
|
|
final int uidCount = mHistoricalUidOps.size();
|
|
parcel.writeInt(uidCount);
|
|
for (int i = 0; i < uidCount; i++) {
|
|
parcel.writeInt(mHistoricalUidOps.keyAt(i));
|
|
}
|
|
final List<HistoricalUidOps> opsList = new ArrayList<>(uidCount);
|
|
for (int i = 0; i < uidCount; i++) {
|
|
opsList.add(mHistoricalUidOps.valueAt(i));
|
|
}
|
|
parcel.writeParcelable(new ParceledListSlice<>(opsList), flags);
|
|
} else {
|
|
parcel.writeInt(-1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Accepts a visitor to traverse the ops tree.
|
|
*
|
|
* @param visitor The visitor.
|
|
*
|
|
* @hide
|
|
*/
|
|
public void accept(@NonNull HistoricalOpsVisitor visitor) {
|
|
visitor.visitHistoricalOps(this);
|
|
final int uidCount = getUidCount();
|
|
for (int i = 0; i < uidCount; i++) {
|
|
getUidOpsAt(i).accept(visitor);
|
|
}
|
|
}
|
|
|
|
private @NonNull HistoricalUidOps getOrCreateHistoricalUidOps(int uid) {
|
|
if (mHistoricalUidOps == null) {
|
|
mHistoricalUidOps = new SparseArray<>();
|
|
}
|
|
HistoricalUidOps historicalUidOp = mHistoricalUidOps.get(uid);
|
|
if (historicalUidOp == null) {
|
|
historicalUidOp = new HistoricalUidOps(uid);
|
|
mHistoricalUidOps.put(uid, historicalUidOp);
|
|
}
|
|
return historicalUidOp;
|
|
}
|
|
|
|
/**
|
|
* @return Rounded value up at the 0.5 boundary.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static double round(double value) {
|
|
return Math.floor(value + 0.5);
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(@Nullable Object obj) {
|
|
if (this == obj) {
|
|
return true;
|
|
}
|
|
if (obj == null || getClass() != obj.getClass()) {
|
|
return false;
|
|
}
|
|
final HistoricalOps other = (HistoricalOps) obj;
|
|
if (mBeginTimeMillis != other.mBeginTimeMillis) {
|
|
return false;
|
|
}
|
|
if (mEndTimeMillis != other.mEndTimeMillis) {
|
|
return false;
|
|
}
|
|
if (mHistoricalUidOps == null) {
|
|
if (other.mHistoricalUidOps != null) {
|
|
return false;
|
|
}
|
|
} else if (!mHistoricalUidOps.equals(other.mHistoricalUidOps)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
int result = (int) (mBeginTimeMillis ^ (mBeginTimeMillis >>> 32));
|
|
result = 31 * result + mHistoricalUidOps.hashCode();
|
|
return result;
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
public String toString() {
|
|
return getClass().getSimpleName() + "[from:"
|
|
+ mBeginTimeMillis + " to:" + mEndTimeMillis + "]";
|
|
}
|
|
|
|
public static final @android.annotation.NonNull Creator<HistoricalOps> CREATOR = new Creator<HistoricalOps>() {
|
|
@Override
|
|
public @NonNull HistoricalOps createFromParcel(@NonNull Parcel parcel) {
|
|
return new HistoricalOps(parcel);
|
|
}
|
|
|
|
@Override
|
|
public @NonNull HistoricalOps[] newArray(int size) {
|
|
return new HistoricalOps[size];
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* This class represents historical app op state for a UID.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public static final class HistoricalUidOps implements Parcelable {
|
|
private final int mUid;
|
|
private @Nullable ArrayMap<String, HistoricalPackageOps> mHistoricalPackageOps;
|
|
|
|
/** @hide */
|
|
public HistoricalUidOps(int uid) {
|
|
mUid = uid;
|
|
}
|
|
|
|
private HistoricalUidOps(@NonNull HistoricalUidOps other) {
|
|
mUid = other.mUid;
|
|
final int opCount = other.getPackageCount();
|
|
for (int i = 0; i < opCount; i++) {
|
|
final HistoricalPackageOps origOps = other.getPackageOpsAt(i);
|
|
final HistoricalPackageOps cloneOps = new HistoricalPackageOps(origOps);
|
|
if (mHistoricalPackageOps == null) {
|
|
mHistoricalPackageOps = new ArrayMap<>(opCount);
|
|
}
|
|
mHistoricalPackageOps.put(cloneOps.getPackageName(), cloneOps);
|
|
}
|
|
}
|
|
|
|
private HistoricalUidOps(@NonNull Parcel parcel) {
|
|
// No arg check since we always read from a trusted source.
|
|
mUid = parcel.readInt();
|
|
mHistoricalPackageOps = parcel.createTypedArrayMap(HistoricalPackageOps.CREATOR);
|
|
}
|
|
|
|
private @Nullable HistoricalUidOps splice(double fractionToRemove) {
|
|
HistoricalUidOps splice = null;
|
|
final int packageCount = getPackageCount();
|
|
for (int i = 0; i < packageCount; i++) {
|
|
final HistoricalPackageOps origOps = getPackageOpsAt(i);
|
|
final HistoricalPackageOps spliceOps = origOps.splice(fractionToRemove);
|
|
if (spliceOps != null) {
|
|
if (splice == null) {
|
|
splice = new HistoricalUidOps(mUid);
|
|
}
|
|
if (splice.mHistoricalPackageOps == null) {
|
|
splice.mHistoricalPackageOps = new ArrayMap<>();
|
|
}
|
|
splice.mHistoricalPackageOps.put(spliceOps.getPackageName(), spliceOps);
|
|
}
|
|
}
|
|
return splice;
|
|
}
|
|
|
|
private void merge(@NonNull HistoricalUidOps other) {
|
|
final int packageCount = other.getPackageCount();
|
|
for (int i = 0; i < packageCount; i++) {
|
|
final HistoricalPackageOps otherPackageOps = other.getPackageOpsAt(i);
|
|
final HistoricalPackageOps thisPackageOps = getPackageOps(
|
|
otherPackageOps.getPackageName());
|
|
if (thisPackageOps != null) {
|
|
thisPackageOps.merge(otherPackageOps);
|
|
} else {
|
|
if (mHistoricalPackageOps == null) {
|
|
mHistoricalPackageOps = new ArrayMap<>();
|
|
}
|
|
mHistoricalPackageOps.put(otherPackageOps.getPackageName(), otherPackageOps);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void filter(@Nullable String packageName, @Nullable String attributionTag,
|
|
@Nullable String[] opNames, @HistoricalOpsRequestFilter int filter,
|
|
@OpHistoryFlags int historyFilter, double fractionToRemove, long beginTimeMillis,
|
|
long endTimeMillis) {
|
|
final int packageCount = getPackageCount();
|
|
for (int i = packageCount - 1; i >= 0; i--) {
|
|
final HistoricalPackageOps packageOps = getPackageOpsAt(i);
|
|
if ((filter & FILTER_BY_PACKAGE_NAME) != 0 && !packageName.equals(
|
|
packageOps.getPackageName())) {
|
|
mHistoricalPackageOps.removeAt(i);
|
|
} else {
|
|
packageOps.filter(attributionTag, opNames, filter, historyFilter,
|
|
fractionToRemove, beginTimeMillis, endTimeMillis);
|
|
if (packageOps.getAttributedOpsCount() == 0) {
|
|
mHistoricalPackageOps.removeAt(i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private boolean isEmpty() {
|
|
final int packageCount = getPackageCount();
|
|
for (int i = packageCount - 1; i >= 0; i--) {
|
|
final HistoricalPackageOps packageOps = mHistoricalPackageOps.valueAt(i);
|
|
if (!packageOps.isEmpty()) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private void increaseAccessCount(int opCode, @NonNull String packageName,
|
|
@Nullable String attributionTag, @UidState int uidState, @OpFlags int flags,
|
|
long increment) {
|
|
getOrCreateHistoricalPackageOps(packageName).increaseAccessCount(
|
|
opCode, attributionTag, uidState, flags, increment);
|
|
}
|
|
|
|
private void increaseRejectCount(int opCode, @NonNull String packageName,
|
|
@Nullable String attributionTag, @UidState int uidState, @OpFlags int flags,
|
|
long increment) {
|
|
getOrCreateHistoricalPackageOps(packageName).increaseRejectCount(
|
|
opCode, attributionTag, uidState, flags, increment);
|
|
}
|
|
|
|
private void increaseAccessDuration(int opCode, @NonNull String packageName,
|
|
@Nullable String attributionTag, @UidState int uidState, @OpFlags int flags,
|
|
long increment) {
|
|
getOrCreateHistoricalPackageOps(packageName).increaseAccessDuration(
|
|
opCode, attributionTag, uidState, flags, increment);
|
|
}
|
|
|
|
private void addDiscreteAccess(int opCode, @NonNull String packageName,
|
|
@Nullable String attributionTag, @UidState int uidState,
|
|
@OpFlags int flag, long discreteAccessTime, long discreteAccessDuration,
|
|
@Nullable OpEventProxyInfo proxy) {
|
|
getOrCreateHistoricalPackageOps(packageName).addDiscreteAccess(opCode, attributionTag,
|
|
uidState, flag, discreteAccessTime, discreteAccessDuration, proxy);
|
|
};
|
|
|
|
/**
|
|
* @return The UID for which the data is related.
|
|
*/
|
|
public int getUid() {
|
|
return mUid;
|
|
}
|
|
|
|
/**
|
|
* Gets number of packages with historical ops.
|
|
*
|
|
* @return The number of packages with historical ops.
|
|
*
|
|
* @see #getPackageOpsAt(int)
|
|
*/
|
|
public @IntRange(from = 0) int getPackageCount() {
|
|
if (mHistoricalPackageOps == null) {
|
|
return 0;
|
|
}
|
|
return mHistoricalPackageOps.size();
|
|
}
|
|
|
|
/**
|
|
* Gets the historical package ops at a given index.
|
|
*
|
|
* @param index The index.
|
|
*
|
|
* @return The historical package ops at the given index.
|
|
*
|
|
* @see #getPackageCount()
|
|
*/
|
|
public @NonNull HistoricalPackageOps getPackageOpsAt(@IntRange(from = 0) int index) {
|
|
if (mHistoricalPackageOps == null) {
|
|
throw new IndexOutOfBoundsException();
|
|
}
|
|
return mHistoricalPackageOps.valueAt(index);
|
|
}
|
|
|
|
/**
|
|
* Gets the historical package ops for a given package.
|
|
*
|
|
* @param packageName The package.
|
|
*
|
|
* @return The historical ops for the package.
|
|
*/
|
|
public @Nullable HistoricalPackageOps getPackageOps(@NonNull String packageName) {
|
|
if (mHistoricalPackageOps == null) {
|
|
return null;
|
|
}
|
|
return mHistoricalPackageOps.get(packageName);
|
|
}
|
|
|
|
private void clearHistory(@NonNull String packageName) {
|
|
if (mHistoricalPackageOps != null) {
|
|
mHistoricalPackageOps.remove(packageName);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int describeContents() {
|
|
return 0;
|
|
}
|
|
|
|
@Override
|
|
public void writeToParcel(Parcel parcel, int flags) {
|
|
parcel.writeInt(mUid);
|
|
parcel.writeTypedArrayMap(mHistoricalPackageOps, flags);
|
|
}
|
|
|
|
private void accept(@NonNull HistoricalOpsVisitor visitor) {
|
|
visitor.visitHistoricalUidOps(this);
|
|
final int packageCount = getPackageCount();
|
|
for (int i = 0; i < packageCount; i++) {
|
|
getPackageOpsAt(i).accept(visitor);
|
|
}
|
|
}
|
|
|
|
private @NonNull HistoricalPackageOps getOrCreateHistoricalPackageOps(
|
|
@NonNull String packageName) {
|
|
if (mHistoricalPackageOps == null) {
|
|
mHistoricalPackageOps = new ArrayMap<>();
|
|
}
|
|
HistoricalPackageOps historicalPackageOp = mHistoricalPackageOps.get(packageName);
|
|
if (historicalPackageOp == null) {
|
|
historicalPackageOp = new HistoricalPackageOps(packageName);
|
|
mHistoricalPackageOps.put(packageName, historicalPackageOp);
|
|
}
|
|
return historicalPackageOp;
|
|
}
|
|
|
|
|
|
public static final @android.annotation.NonNull Creator<HistoricalUidOps> CREATOR = new Creator<HistoricalUidOps>() {
|
|
@Override
|
|
public @NonNull HistoricalUidOps createFromParcel(@NonNull Parcel parcel) {
|
|
return new HistoricalUidOps(parcel);
|
|
}
|
|
|
|
@Override
|
|
public @NonNull HistoricalUidOps[] newArray(int size) {
|
|
return new HistoricalUidOps[size];
|
|
}
|
|
};
|
|
|
|
@Override
|
|
public boolean equals(@Nullable Object obj) {
|
|
if (this == obj) {
|
|
return true;
|
|
}
|
|
if (obj == null || getClass() != obj.getClass()) {
|
|
return false;
|
|
}
|
|
final HistoricalUidOps other = (HistoricalUidOps) obj;
|
|
if (mUid != other.mUid) {
|
|
return false;
|
|
}
|
|
if (mHistoricalPackageOps == null) {
|
|
if (other.mHistoricalPackageOps != null) {
|
|
return false;
|
|
}
|
|
} else if (!mHistoricalPackageOps.equals(other.mHistoricalPackageOps)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
int result = mUid;
|
|
result = 31 * result + (mHistoricalPackageOps != null
|
|
? mHistoricalPackageOps.hashCode() : 0);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This class represents historical app op information about a package.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public static final class HistoricalPackageOps implements Parcelable {
|
|
private final @NonNull String mPackageName;
|
|
private @Nullable ArrayMap<String, AttributedHistoricalOps> mAttributedHistoricalOps;
|
|
|
|
/** @hide */
|
|
public HistoricalPackageOps(@NonNull String packageName) {
|
|
mPackageName = packageName;
|
|
}
|
|
|
|
private HistoricalPackageOps(@NonNull HistoricalPackageOps other) {
|
|
mPackageName = other.mPackageName;
|
|
final int opCount = other.getAttributedOpsCount();
|
|
for (int i = 0; i < opCount; i++) {
|
|
final AttributedHistoricalOps origOps = other.getAttributedOpsAt(i);
|
|
final AttributedHistoricalOps cloneOps = new AttributedHistoricalOps(origOps);
|
|
if (mAttributedHistoricalOps == null) {
|
|
mAttributedHistoricalOps = new ArrayMap<>(opCount);
|
|
}
|
|
mAttributedHistoricalOps.put(cloneOps.getTag(), cloneOps);
|
|
}
|
|
}
|
|
|
|
private HistoricalPackageOps(@NonNull Parcel parcel) {
|
|
mPackageName = parcel.readString();
|
|
mAttributedHistoricalOps = parcel.createTypedArrayMap(AttributedHistoricalOps.CREATOR);
|
|
}
|
|
|
|
private @Nullable HistoricalPackageOps splice(double fractionToRemove) {
|
|
HistoricalPackageOps splice = null;
|
|
final int attributionCount = getAttributedOpsCount();
|
|
for (int i = 0; i < attributionCount; i++) {
|
|
final AttributedHistoricalOps origOps = getAttributedOpsAt(i);
|
|
final AttributedHistoricalOps spliceOps = origOps.splice(fractionToRemove);
|
|
if (spliceOps != null) {
|
|
if (splice == null) {
|
|
splice = new HistoricalPackageOps(mPackageName);
|
|
}
|
|
if (splice.mAttributedHistoricalOps == null) {
|
|
splice.mAttributedHistoricalOps = new ArrayMap<>();
|
|
}
|
|
splice.mAttributedHistoricalOps.put(spliceOps.getTag(), spliceOps);
|
|
}
|
|
}
|
|
return splice;
|
|
}
|
|
|
|
private void merge(@NonNull HistoricalPackageOps other) {
|
|
final int attributionCount = other.getAttributedOpsCount();
|
|
for (int i = 0; i < attributionCount; i++) {
|
|
final AttributedHistoricalOps otherAttributionOps = other.getAttributedOpsAt(i);
|
|
final AttributedHistoricalOps thisAttributionOps = getAttributedOps(
|
|
otherAttributionOps.getTag());
|
|
if (thisAttributionOps != null) {
|
|
thisAttributionOps.merge(otherAttributionOps);
|
|
} else {
|
|
if (mAttributedHistoricalOps == null) {
|
|
mAttributedHistoricalOps = new ArrayMap<>();
|
|
}
|
|
mAttributedHistoricalOps.put(otherAttributionOps.getTag(),
|
|
otherAttributionOps);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void filter(@Nullable String attributionTag, @Nullable String[] opNames,
|
|
@HistoricalOpsRequestFilter int filter, @OpHistoryFlags int historyFilter,
|
|
double fractionToRemove, long beginTimeMillis, long endTimeMillis) {
|
|
final int attributionCount = getAttributedOpsCount();
|
|
for (int i = attributionCount - 1; i >= 0; i--) {
|
|
final AttributedHistoricalOps attributionOps = getAttributedOpsAt(i);
|
|
if ((filter & FILTER_BY_ATTRIBUTION_TAG) != 0 && !Objects.equals(attributionTag,
|
|
attributionOps.getTag())) {
|
|
mAttributedHistoricalOps.removeAt(i);
|
|
} else {
|
|
attributionOps.filter(opNames, filter, historyFilter, fractionToRemove,
|
|
beginTimeMillis, endTimeMillis);
|
|
if (attributionOps.getOpCount() == 0) {
|
|
mAttributedHistoricalOps.removeAt(i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void accept(@NonNull HistoricalOpsVisitor visitor) {
|
|
visitor.visitHistoricalPackageOps(this);
|
|
final int attributionCount = getAttributedOpsCount();
|
|
for (int i = 0; i < attributionCount; i++) {
|
|
getAttributedOpsAt(i).accept(visitor);
|
|
}
|
|
}
|
|
|
|
private boolean isEmpty() {
|
|
final int attributionCount = getAttributedOpsCount();
|
|
for (int i = attributionCount - 1; i >= 0; i--) {
|
|
final AttributedHistoricalOps attributionOps = mAttributedHistoricalOps.valueAt(i);
|
|
if (!attributionOps.isEmpty()) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private void increaseAccessCount(int opCode, @Nullable String attributionTag,
|
|
@UidState int uidState, @OpFlags int flags, long increment) {
|
|
getOrCreateAttributedHistoricalOps(attributionTag).increaseAccessCount(
|
|
opCode, uidState, flags, increment);
|
|
}
|
|
|
|
private void increaseRejectCount(int opCode, @Nullable String attributionTag,
|
|
@UidState int uidState, @OpFlags int flags, long increment) {
|
|
getOrCreateAttributedHistoricalOps(attributionTag).increaseRejectCount(
|
|
opCode, uidState, flags, increment);
|
|
}
|
|
|
|
private void increaseAccessDuration(int opCode, @Nullable String attributionTag,
|
|
@UidState int uidState, @OpFlags int flags, long increment) {
|
|
getOrCreateAttributedHistoricalOps(attributionTag).increaseAccessDuration(
|
|
opCode, uidState, flags, increment);
|
|
}
|
|
|
|
private void addDiscreteAccess(int opCode, @Nullable String attributionTag,
|
|
@UidState int uidState, @OpFlags int flag, long discreteAccessTime,
|
|
long discreteAccessDuration, @Nullable OpEventProxyInfo proxy) {
|
|
getOrCreateAttributedHistoricalOps(attributionTag).addDiscreteAccess(opCode, uidState,
|
|
flag, discreteAccessTime, discreteAccessDuration, proxy);
|
|
}
|
|
|
|
/**
|
|
* Gets the package name which the data represents.
|
|
*
|
|
* @return The package name which the data represents.
|
|
*/
|
|
public @NonNull String getPackageName() {
|
|
return mPackageName;
|
|
}
|
|
|
|
private @NonNull AttributedHistoricalOps getOrCreateAttributedHistoricalOps(
|
|
@Nullable String attributionTag) {
|
|
if (mAttributedHistoricalOps == null) {
|
|
mAttributedHistoricalOps = new ArrayMap<>();
|
|
}
|
|
AttributedHistoricalOps historicalAttributionOp = mAttributedHistoricalOps.get(
|
|
attributionTag);
|
|
if (historicalAttributionOp == null) {
|
|
historicalAttributionOp = new AttributedHistoricalOps(attributionTag);
|
|
mAttributedHistoricalOps.put(attributionTag, historicalAttributionOp);
|
|
}
|
|
return historicalAttributionOp;
|
|
}
|
|
|
|
/**
|
|
* Gets number historical app ops.
|
|
*
|
|
* @return The number historical app ops.
|
|
* @see #getOpAt(int)
|
|
*/
|
|
public @IntRange(from = 0) int getOpCount() {
|
|
int numOps = 0;
|
|
int numAttributions = getAttributedOpsCount();
|
|
|
|
for (int code = 0; code < _NUM_OP; code++) {
|
|
String opName = opToPublicName(code);
|
|
|
|
for (int attributionNum = 0; attributionNum < numAttributions; attributionNum++) {
|
|
if (getAttributedOpsAt(attributionNum).getOp(opName) != null) {
|
|
numOps++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return numOps;
|
|
}
|
|
|
|
/**
|
|
* Gets the historical op at a given index.
|
|
*
|
|
* <p>This combines the counts from all attributions.
|
|
*
|
|
* @param index The index to lookup.
|
|
* @return The op at the given index.
|
|
* @see #getOpCount()
|
|
*/
|
|
public @NonNull HistoricalOp getOpAt(@IntRange(from = 0) int index) {
|
|
int numOpsFound = 0;
|
|
int numAttributions = getAttributedOpsCount();
|
|
|
|
for (int code = 0; code < _NUM_OP; code++) {
|
|
String opName = opToPublicName(code);
|
|
|
|
for (int attributionNum = 0; attributionNum < numAttributions; attributionNum++) {
|
|
if (getAttributedOpsAt(attributionNum).getOp(opName) != null) {
|
|
if (numOpsFound == index) {
|
|
return getOp(opName);
|
|
} else {
|
|
numOpsFound++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
throw new IndexOutOfBoundsException();
|
|
}
|
|
|
|
/**
|
|
* Gets the historical entry for a given op name.
|
|
*
|
|
* <p>This combines the counts from all attributions.
|
|
*
|
|
* @param opName The op name.
|
|
* @return The historical entry for that op name.
|
|
*/
|
|
public @Nullable HistoricalOp getOp(@NonNull String opName) {
|
|
if (mAttributedHistoricalOps == null) {
|
|
return null;
|
|
}
|
|
|
|
HistoricalOp combinedOp = null;
|
|
int numAttributions = getAttributedOpsCount();
|
|
for (int i = 0; i < numAttributions; i++) {
|
|
HistoricalOp attributionOp = getAttributedOpsAt(i).getOp(opName);
|
|
if (attributionOp != null) {
|
|
if (combinedOp == null) {
|
|
combinedOp = new HistoricalOp(attributionOp);
|
|
} else {
|
|
combinedOp.merge(attributionOp);
|
|
}
|
|
}
|
|
}
|
|
|
|
return combinedOp;
|
|
}
|
|
|
|
@Override
|
|
public int describeContents() {
|
|
return 0;
|
|
}
|
|
|
|
@Override
|
|
public void writeToParcel(@NonNull Parcel parcel, int flags) {
|
|
parcel.writeString(mPackageName);
|
|
parcel.writeTypedArrayMap(mAttributedHistoricalOps, flags);
|
|
}
|
|
|
|
public static final @android.annotation.NonNull Creator<HistoricalPackageOps> CREATOR =
|
|
new Creator<HistoricalPackageOps>() {
|
|
@Override
|
|
public @NonNull HistoricalPackageOps createFromParcel(@NonNull Parcel parcel) {
|
|
return new HistoricalPackageOps(parcel);
|
|
}
|
|
|
|
@Override
|
|
public @NonNull HistoricalPackageOps[] newArray(int size) {
|
|
return new HistoricalPackageOps[size];
|
|
}
|
|
};
|
|
|
|
@Override
|
|
public boolean equals(@Nullable Object obj) {
|
|
if (this == obj) {
|
|
return true;
|
|
}
|
|
if (obj == null || getClass() != obj.getClass()) {
|
|
return false;
|
|
}
|
|
final HistoricalPackageOps other = (HistoricalPackageOps) obj;
|
|
if (!mPackageName.equals(other.mPackageName)) {
|
|
return false;
|
|
}
|
|
if (mAttributedHistoricalOps == null) {
|
|
if (other.mAttributedHistoricalOps != null) {
|
|
return false;
|
|
}
|
|
} else if (!mAttributedHistoricalOps.equals(other.mAttributedHistoricalOps)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
int result = mPackageName != null ? mPackageName.hashCode() : 0;
|
|
result = 31 * result + (mAttributedHistoricalOps != null
|
|
? mAttributedHistoricalOps.hashCode() : 0);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Gets number of attributed historical ops.
|
|
*
|
|
* @return The number of attribution with historical ops.
|
|
*
|
|
* @see #getAttributedOpsAt(int)
|
|
*/
|
|
public @IntRange(from = 0) int getAttributedOpsCount() {
|
|
if (mAttributedHistoricalOps == null) {
|
|
return 0;
|
|
}
|
|
return mAttributedHistoricalOps.size();
|
|
}
|
|
|
|
/**
|
|
* Gets the attributed historical ops at a given index.
|
|
*
|
|
* @param index The index.
|
|
*
|
|
* @return The historical attribution ops at the given index.
|
|
*
|
|
* @see #getAttributedOpsCount()
|
|
*/
|
|
public @NonNull AttributedHistoricalOps getAttributedOpsAt(@IntRange(from = 0) int index) {
|
|
if (mAttributedHistoricalOps == null) {
|
|
throw new IndexOutOfBoundsException();
|
|
}
|
|
return mAttributedHistoricalOps.valueAt(index);
|
|
}
|
|
|
|
/**
|
|
* Gets the attributed historical ops for a given attribution tag.
|
|
*
|
|
* @param attributionTag The attribution tag.
|
|
*
|
|
* @return The historical ops for the attribution.
|
|
*/
|
|
public @Nullable AttributedHistoricalOps getAttributedOps(@Nullable String attributionTag) {
|
|
if (mAttributedHistoricalOps == null) {
|
|
return null;
|
|
}
|
|
return mAttributedHistoricalOps.get(attributionTag);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This class represents historical app op information about a attribution in a package.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
/* codegen verifier cannot deal with nested class parameters
|
|
@DataClass(genHiddenConstructor = true,
|
|
genEqualsHashCode = true, genHiddenCopyConstructor = true) */
|
|
@DataClass.Suppress("getHistoricalOps")
|
|
public static final class AttributedHistoricalOps implements Parcelable {
|
|
/** {@link Context#createAttributionContext attribution} tag */
|
|
private final @Nullable String mTag;
|
|
|
|
/** Ops for this attribution */
|
|
private @Nullable ArrayMap<String, HistoricalOp> mHistoricalOps;
|
|
|
|
/** @hide */
|
|
public AttributedHistoricalOps(@NonNull String tag) {
|
|
mTag = tag;
|
|
}
|
|
|
|
private AttributedHistoricalOps(@NonNull AttributedHistoricalOps other) {
|
|
mTag = other.mTag;
|
|
final int opCount = other.getOpCount();
|
|
for (int i = 0; i < opCount; i++) {
|
|
final HistoricalOp origOp = other.getOpAt(i);
|
|
final HistoricalOp cloneOp = new HistoricalOp(origOp);
|
|
if (mHistoricalOps == null) {
|
|
mHistoricalOps = new ArrayMap<>(opCount);
|
|
}
|
|
mHistoricalOps.put(cloneOp.getOpName(), cloneOp);
|
|
}
|
|
}
|
|
|
|
private @Nullable AttributedHistoricalOps splice(double fractionToRemove) {
|
|
AttributedHistoricalOps splice = null;
|
|
final int opCount = getOpCount();
|
|
for (int i = 0; i < opCount; i++) {
|
|
final HistoricalOp origOps = getOpAt(i);
|
|
final HistoricalOp spliceOps = origOps.splice(fractionToRemove);
|
|
if (spliceOps != null) {
|
|
if (splice == null) {
|
|
splice = new AttributedHistoricalOps(mTag, null);
|
|
}
|
|
if (splice.mHistoricalOps == null) {
|
|
splice.mHistoricalOps = new ArrayMap<>();
|
|
}
|
|
splice.mHistoricalOps.put(spliceOps.getOpName(), spliceOps);
|
|
}
|
|
}
|
|
return splice;
|
|
}
|
|
|
|
private void merge(@NonNull AttributedHistoricalOps other) {
|
|
final int opCount = other.getOpCount();
|
|
for (int i = 0; i < opCount; i++) {
|
|
final HistoricalOp otherOp = other.getOpAt(i);
|
|
final HistoricalOp thisOp = getOp(otherOp.getOpName());
|
|
if (thisOp != null) {
|
|
thisOp.merge(otherOp);
|
|
} else {
|
|
if (mHistoricalOps == null) {
|
|
mHistoricalOps = new ArrayMap<>();
|
|
}
|
|
mHistoricalOps.put(otherOp.getOpName(), otherOp);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void filter(@Nullable String[] opNames, @HistoricalOpsRequestFilter int filter,
|
|
@OpHistoryFlags int historyFilter, double scaleFactor, long beginTimeMillis,
|
|
long endTimeMillis) {
|
|
final int opCount = getOpCount();
|
|
for (int i = opCount - 1; i >= 0; i--) {
|
|
final HistoricalOp op = mHistoricalOps.valueAt(i);
|
|
if ((filter & FILTER_BY_OP_NAMES) != 0 && !ArrayUtils.contains(opNames,
|
|
op.getOpName())) {
|
|
mHistoricalOps.removeAt(i);
|
|
} else {
|
|
op.filter(historyFilter, scaleFactor, beginTimeMillis, endTimeMillis);
|
|
}
|
|
}
|
|
}
|
|
|
|
private boolean isEmpty() {
|
|
final int opCount = getOpCount();
|
|
for (int i = opCount - 1; i >= 0; i--) {
|
|
final HistoricalOp op = mHistoricalOps.valueAt(i);
|
|
if (!op.isEmpty()) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private void increaseAccessCount(int opCode, @UidState int uidState,
|
|
@OpFlags int flags, long increment) {
|
|
getOrCreateHistoricalOp(opCode).increaseAccessCount(uidState, flags, increment);
|
|
}
|
|
|
|
private void increaseRejectCount(int opCode, @UidState int uidState,
|
|
@OpFlags int flags, long increment) {
|
|
getOrCreateHistoricalOp(opCode).increaseRejectCount(uidState, flags, increment);
|
|
}
|
|
|
|
private void increaseAccessDuration(int opCode, @UidState int uidState,
|
|
@OpFlags int flags, long increment) {
|
|
getOrCreateHistoricalOp(opCode).increaseAccessDuration(uidState, flags, increment);
|
|
}
|
|
|
|
private void addDiscreteAccess(int opCode, @UidState int uidState, @OpFlags int flag,
|
|
long discreteAccessTime, long discreteAccessDuration,
|
|
@Nullable OpEventProxyInfo proxy) {
|
|
getOrCreateHistoricalOp(opCode).addDiscreteAccess(uidState,flag, discreteAccessTime,
|
|
discreteAccessDuration, proxy);
|
|
}
|
|
|
|
/**
|
|
* Gets number historical app ops.
|
|
*
|
|
* @return The number historical app ops.
|
|
* @see #getOpAt(int)
|
|
*/
|
|
public @IntRange(from = 0) int getOpCount() {
|
|
if (mHistoricalOps == null) {
|
|
return 0;
|
|
}
|
|
return mHistoricalOps.size();
|
|
}
|
|
|
|
/**
|
|
* Gets the historical op at a given index.
|
|
*
|
|
* @param index The index to lookup.
|
|
* @return The op at the given index.
|
|
* @see #getOpCount()
|
|
*/
|
|
public @NonNull HistoricalOp getOpAt(@IntRange(from = 0) int index) {
|
|
if (mHistoricalOps == null) {
|
|
throw new IndexOutOfBoundsException();
|
|
}
|
|
return mHistoricalOps.valueAt(index);
|
|
}
|
|
|
|
/**
|
|
* Gets the historical entry for a given op name.
|
|
*
|
|
* @param opName The op name.
|
|
* @return The historical entry for that op name.
|
|
*/
|
|
public @Nullable HistoricalOp getOp(@NonNull String opName) {
|
|
if (mHistoricalOps == null) {
|
|
return null;
|
|
}
|
|
return mHistoricalOps.get(opName);
|
|
}
|
|
|
|
private void accept(@NonNull HistoricalOpsVisitor visitor) {
|
|
visitor.visitHistoricalAttributionOps(this);
|
|
final int opCount = getOpCount();
|
|
for (int i = 0; i < opCount; i++) {
|
|
getOpAt(i).accept(visitor);
|
|
}
|
|
}
|
|
|
|
private @NonNull HistoricalOp getOrCreateHistoricalOp(int opCode) {
|
|
if (mHistoricalOps == null) {
|
|
mHistoricalOps = new ArrayMap<>();
|
|
}
|
|
final String opStr = sAppOpInfos[opCode].name;
|
|
HistoricalOp op = mHistoricalOps.get(opStr);
|
|
if (op == null) {
|
|
op = new HistoricalOp(opCode);
|
|
mHistoricalOps.put(opStr, op);
|
|
}
|
|
return op;
|
|
}
|
|
|
|
// Code below generated by codegen v1.0.14.
|
|
//
|
|
// DO NOT MODIFY!
|
|
// CHECKSTYLE:OFF Generated code
|
|
//
|
|
// To regenerate run:
|
|
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/app/AppOpsManager.java
|
|
//
|
|
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
|
|
// Settings > Editor > Code Style > Formatter Control
|
|
//@formatter:off
|
|
|
|
|
|
/**
|
|
* Creates a new HistoricalAttributionOps.
|
|
*
|
|
* @param tag
|
|
* {@link Context#createAttributionContext attribution} tag
|
|
* @param historicalOps
|
|
* Ops for this attribution
|
|
* @hide
|
|
*/
|
|
@DataClass.Generated.Member
|
|
public AttributedHistoricalOps(
|
|
@Nullable String tag,
|
|
@Nullable ArrayMap<String,HistoricalOp> historicalOps) {
|
|
this.mTag = tag;
|
|
this.mHistoricalOps = historicalOps;
|
|
|
|
// onConstructed(); // You can define this method to get a callback
|
|
}
|
|
|
|
/**
|
|
* {@link Context#createAttributionContext attribution} tag
|
|
*/
|
|
@DataClass.Generated.Member
|
|
public @Nullable String getTag() {
|
|
return mTag;
|
|
}
|
|
|
|
@Override
|
|
@DataClass.Generated.Member
|
|
public boolean equals(@Nullable Object o) {
|
|
// You can override field equality logic by defining either of the methods like:
|
|
// boolean fieldNameEquals(HistoricalAttributionOps other) { ... }
|
|
// boolean fieldNameEquals(FieldType otherValue) { ... }
|
|
|
|
if (this == o) return true;
|
|
if (o == null || getClass() != o.getClass()) return false;
|
|
@SuppressWarnings("unchecked")
|
|
AttributedHistoricalOps that = (AttributedHistoricalOps) o;
|
|
//noinspection PointlessBooleanExpression
|
|
return true
|
|
&& Objects.equals(mTag, that.mTag)
|
|
&& Objects.equals(mHistoricalOps, that.mHistoricalOps);
|
|
}
|
|
|
|
@Override
|
|
@DataClass.Generated.Member
|
|
public int hashCode() {
|
|
// You can override field hashCode logic by defining methods like:
|
|
// int fieldNameHashCode() { ... }
|
|
|
|
int _hash = 1;
|
|
_hash = 31 * _hash + Objects.hashCode(mTag);
|
|
_hash = 31 * _hash + Objects.hashCode(mHistoricalOps);
|
|
return _hash;
|
|
}
|
|
|
|
@Override
|
|
@DataClass.Generated.Member
|
|
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
|
// You can override field parcelling by defining methods like:
|
|
// void parcelFieldName(Parcel dest, int flags) { ... }
|
|
|
|
byte flg = 0;
|
|
if (mTag != null) flg |= 0x1;
|
|
if (mHistoricalOps != null) flg |= 0x2;
|
|
dest.writeByte(flg);
|
|
if (mTag != null) dest.writeString(mTag);
|
|
if (mHistoricalOps != null) dest.writeMap(mHistoricalOps);
|
|
}
|
|
|
|
@Override
|
|
@DataClass.Generated.Member
|
|
public int describeContents() { return 0; }
|
|
|
|
/** @hide */
|
|
@SuppressWarnings({"unchecked", "RedundantCast"})
|
|
@DataClass.Generated.Member
|
|
/* package-private */ AttributedHistoricalOps(@NonNull Parcel in) {
|
|
// You can override field unparcelling by defining methods like:
|
|
// static FieldType unparcelFieldName(Parcel in) { ... }
|
|
|
|
byte flg = in.readByte();
|
|
String attributionTag = (flg & 0x1) == 0 ? null : in.readString();
|
|
ArrayMap<String,HistoricalOp> historicalOps = null;
|
|
if ((flg & 0x2) != 0) {
|
|
historicalOps = new ArrayMap();
|
|
in.readMap(historicalOps, HistoricalOp.class.getClassLoader());
|
|
}
|
|
|
|
this.mTag = attributionTag;
|
|
this.mHistoricalOps = historicalOps;
|
|
|
|
// onConstructed(); // You can define this method to get a callback
|
|
}
|
|
|
|
@DataClass.Generated.Member
|
|
public static final @NonNull Parcelable.Creator<AttributedHistoricalOps> CREATOR
|
|
= new Parcelable.Creator<AttributedHistoricalOps>() {
|
|
@Override
|
|
public AttributedHistoricalOps[] newArray(int size) {
|
|
return new AttributedHistoricalOps[size];
|
|
}
|
|
|
|
@Override
|
|
public AttributedHistoricalOps createFromParcel(@NonNull Parcel in) {
|
|
return new AttributedHistoricalOps(in);
|
|
}
|
|
};
|
|
|
|
/*
|
|
@DataClass.Generated(
|
|
time = 1578113234821L,
|
|
codegenVersion = "1.0.14",
|
|
sourceFile = "frameworks/base/core/java/android/app/AppOpsManager.java",
|
|
inputSignatures = "private final @android.annotation.Nullable java.lang.String mAttributionTag\nprivate @android.annotation.Nullable android.util.ArrayMap<java.lang.String,android.app.HistoricalOp> mHistoricalOps\nprivate @android.annotation.Nullable android.app.HistoricalAttributionOps splice(double)\nprivate void merge(android.app.HistoricalAttributionOps)\nprivate void filter(java.lang.String[],int,double)\nprivate boolean isEmpty()\nprivate void increaseAccessCount(int,int,int,long)\nprivate void increaseRejectCount(int,int,int,long)\nprivate void increaseAccessDuration(int,int,int,long)\npublic @android.annotation.IntRange(from=0L) int getOpCount()\npublic @android.annotation.NonNull android.app.HistoricalOp getOpAt(int)\npublic @android.annotation.Nullable android.app.HistoricalOp getOp(java.lang.String)\nprivate void accept(android.app.HistoricalOpsVisitor)\nprivate @android.annotation.NonNull android.app.HistoricalOp getOrCreateHistoricalOp(int)\nclass HistoricalAttributionOps extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genEqualsHashCode=true, genHiddenCopyConstructor=true)")
|
|
@Deprecated
|
|
private void __metadata() {}
|
|
*/
|
|
|
|
//@formatter:on
|
|
// End of generated code
|
|
|
|
}
|
|
|
|
/**
|
|
* This class represents historical information about an app op.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public static final class HistoricalOp implements Parcelable {
|
|
private final int mOp;
|
|
private @Nullable LongSparseLongArray mAccessCount;
|
|
private @Nullable LongSparseLongArray mRejectCount;
|
|
private @Nullable LongSparseLongArray mAccessDuration;
|
|
|
|
/** Discrete Ops for this Op */
|
|
private @Nullable List<AttributedOpEntry> mDiscreteAccesses;
|
|
|
|
/** @hide */
|
|
public HistoricalOp(int op) {
|
|
mOp = op;
|
|
}
|
|
|
|
private HistoricalOp(@NonNull HistoricalOp other) {
|
|
mOp = other.mOp;
|
|
if (other.mAccessCount != null) {
|
|
mAccessCount = other.mAccessCount.clone();
|
|
}
|
|
if (other.mRejectCount != null) {
|
|
mRejectCount = other.mRejectCount.clone();
|
|
}
|
|
if (other.mAccessDuration != null) {
|
|
mAccessDuration = other.mAccessDuration.clone();
|
|
}
|
|
final int historicalOpCount = other.getDiscreteAccessCount();
|
|
for (int i = 0; i < historicalOpCount; i++) {
|
|
final AttributedOpEntry origOp = other.getDiscreteAccessAt(i);
|
|
final AttributedOpEntry cloneOp = new AttributedOpEntry(origOp);
|
|
getOrCreateDiscreteAccesses().add(cloneOp);
|
|
}
|
|
}
|
|
|
|
private HistoricalOp(@NonNull Parcel parcel) {
|
|
mOp = parcel.readInt();
|
|
mAccessCount = readLongSparseLongArrayFromParcel(parcel);
|
|
mRejectCount = readLongSparseLongArrayFromParcel(parcel);
|
|
mAccessDuration = readLongSparseLongArrayFromParcel(parcel);
|
|
mDiscreteAccesses = readDiscreteAccessArrayFromParcel(parcel);
|
|
}
|
|
|
|
private void filter(@OpHistoryFlags int historyFlag, double scaleFactor,
|
|
long beginTimeMillis, long endTimeMillis) {
|
|
if ((historyFlag & HISTORY_FLAG_AGGREGATE) == 0) {
|
|
mAccessCount = null;
|
|
mRejectCount = null;
|
|
mAccessDuration = null;
|
|
} else {
|
|
scale(mAccessCount, scaleFactor);
|
|
scale(mRejectCount, scaleFactor);
|
|
scale(mAccessDuration, scaleFactor);
|
|
}
|
|
if ((historyFlag & HISTORY_FLAG_DISCRETE) == 0) {
|
|
mDiscreteAccesses = null;
|
|
return;
|
|
}
|
|
final int discreteOpCount = getDiscreteAccessCount();
|
|
for (int i = discreteOpCount - 1; i >= 0; i--) {
|
|
final AttributedOpEntry op = mDiscreteAccesses.get(i);
|
|
long opBeginTime = op.getLastAccessTime(OP_FLAGS_ALL);
|
|
long opEndTime = opBeginTime + op.getLastDuration(OP_FLAGS_ALL);
|
|
opEndTime = max(opBeginTime, opEndTime);
|
|
if (opEndTime < beginTimeMillis || opBeginTime > endTimeMillis) {
|
|
mDiscreteAccesses.remove(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
private boolean isEmpty() {
|
|
return !hasData(mAccessCount)
|
|
&& !hasData(mRejectCount)
|
|
&& !hasData(mAccessDuration)
|
|
&& (mDiscreteAccesses == null);
|
|
}
|
|
|
|
private boolean hasData(@NonNull LongSparseLongArray array) {
|
|
return array != null && array.size() > 0;
|
|
}
|
|
|
|
private @Nullable HistoricalOp splice(double fractionToRemove) {
|
|
final HistoricalOp splice = new HistoricalOp(mOp);
|
|
splice(mAccessCount, splice::getOrCreateAccessCount, fractionToRemove);
|
|
splice(mRejectCount, splice::getOrCreateRejectCount, fractionToRemove);
|
|
splice(mAccessDuration, splice::getOrCreateAccessDuration, fractionToRemove);
|
|
return splice;
|
|
}
|
|
|
|
private static void splice(@Nullable LongSparseLongArray sourceContainer,
|
|
@NonNull Supplier<LongSparseLongArray> destContainerProvider,
|
|
double fractionToRemove) {
|
|
if (sourceContainer != null) {
|
|
final int size = sourceContainer.size();
|
|
for (int i = 0; i < size; i++) {
|
|
final long key = sourceContainer.keyAt(i);
|
|
final long value = sourceContainer.valueAt(i);
|
|
final long removedFraction = Math.round(value * fractionToRemove);
|
|
if (removedFraction > 0) {
|
|
destContainerProvider.get().put(key, removedFraction);
|
|
sourceContainer.put(key, value - removedFraction);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void merge(@NonNull HistoricalOp other) {
|
|
merge(this::getOrCreateAccessCount, other.mAccessCount);
|
|
merge(this::getOrCreateRejectCount, other.mRejectCount);
|
|
merge(this::getOrCreateAccessDuration, other.mAccessDuration);
|
|
|
|
if (other.mDiscreteAccesses == null) {
|
|
return;
|
|
}
|
|
if (mDiscreteAccesses == null) {
|
|
mDiscreteAccesses = new ArrayList(other.mDiscreteAccesses);
|
|
return;
|
|
}
|
|
List<AttributedOpEntry> historicalDiscreteAccesses = new ArrayList<>();
|
|
final int otherHistoricalOpCount = other.getDiscreteAccessCount();
|
|
final int historicalOpCount = getDiscreteAccessCount();
|
|
int i = 0;
|
|
int j = 0;
|
|
while (i < otherHistoricalOpCount || j < historicalOpCount) {
|
|
if (i == otherHistoricalOpCount) {
|
|
historicalDiscreteAccesses.add(mDiscreteAccesses.get(j++));
|
|
} else if (j == historicalOpCount) {
|
|
historicalDiscreteAccesses.add(other.mDiscreteAccesses.get(i++));
|
|
} else if (mDiscreteAccesses.get(j).getLastAccessTime(OP_FLAGS_ALL)
|
|
< other.mDiscreteAccesses.get(i).getLastAccessTime(OP_FLAGS_ALL)) {
|
|
historicalDiscreteAccesses.add(mDiscreteAccesses.get(j++));
|
|
} else {
|
|
historicalDiscreteAccesses.add(other.mDiscreteAccesses.get(i++));
|
|
}
|
|
}
|
|
mDiscreteAccesses = deduplicateDiscreteEvents(historicalDiscreteAccesses);
|
|
}
|
|
|
|
private void increaseAccessCount(@UidState int uidState, @OpFlags int flags,
|
|
long increment) {
|
|
increaseCount(getOrCreateAccessCount(), uidState, flags, increment);
|
|
}
|
|
|
|
private void increaseRejectCount(@UidState int uidState, @OpFlags int flags,
|
|
long increment) {
|
|
increaseCount(getOrCreateRejectCount(), uidState, flags, increment);
|
|
}
|
|
|
|
private void increaseAccessDuration(@UidState int uidState, @OpFlags int flags,
|
|
long increment) {
|
|
increaseCount(getOrCreateAccessDuration(), uidState, flags, increment);
|
|
}
|
|
|
|
private void increaseCount(@NonNull LongSparseLongArray counts,
|
|
@UidState int uidState, @OpFlags int flags, long increment) {
|
|
while (flags != 0) {
|
|
final int flag = 1 << Integer.numberOfTrailingZeros(flags);
|
|
flags &= ~flag;
|
|
final long key = makeKey(uidState, flag);
|
|
counts.put(key, counts.get(key) + increment);
|
|
}
|
|
}
|
|
|
|
private void addDiscreteAccess(@UidState int uidState, @OpFlags int flag,
|
|
long discreteAccessTime, long discreteAccessDuration,
|
|
@Nullable OpEventProxyInfo proxy) {
|
|
List<AttributedOpEntry> discreteAccesses = getOrCreateDiscreteAccesses();
|
|
LongSparseArray<NoteOpEvent> accessEvents = new LongSparseArray<>();
|
|
long key = makeKey(uidState, flag);
|
|
NoteOpEvent note = new NoteOpEvent(discreteAccessTime, discreteAccessDuration, proxy);
|
|
accessEvents.append(key, note);
|
|
AttributedOpEntry access = new AttributedOpEntry(mOp, false, accessEvents, null);
|
|
int insertionPoint = discreteAccesses.size() - 1;
|
|
for (; insertionPoint >= 0; insertionPoint--) {
|
|
if (discreteAccesses.get(insertionPoint).getLastAccessTime(OP_FLAGS_ALL)
|
|
< discreteAccessTime) {
|
|
break;
|
|
}
|
|
}
|
|
insertionPoint++;
|
|
if (insertionPoint < discreteAccesses.size() && discreteAccesses.get(
|
|
insertionPoint).getLastAccessTime(OP_FLAGS_ALL) == discreteAccessTime) {
|
|
discreteAccesses.set(insertionPoint, mergeAttributedOpEntries(
|
|
Arrays.asList(discreteAccesses.get(insertionPoint), access)));
|
|
} else {
|
|
discreteAccesses.add(insertionPoint, access);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the op name.
|
|
*
|
|
* @return The op name.
|
|
*/
|
|
public @NonNull String getOpName() {
|
|
return sAppOpInfos[mOp].name;
|
|
}
|
|
|
|
/** @hide */
|
|
public int getOpCode() {
|
|
return mOp;
|
|
}
|
|
|
|
/**
|
|
* Gets number of discrete historical app ops.
|
|
*
|
|
* @return The number historical app ops.
|
|
* @see #getDiscreteAccessAt(int)
|
|
*/
|
|
public @IntRange(from = 0) int getDiscreteAccessCount() {
|
|
if (mDiscreteAccesses == null) {
|
|
return 0;
|
|
}
|
|
return mDiscreteAccesses.size();
|
|
}
|
|
|
|
/**
|
|
* Gets the historical op at a given index.
|
|
*
|
|
* @param index The index to lookup.
|
|
* @return The op at the given index.
|
|
* @see #getDiscreteAccessCount()
|
|
*/
|
|
public @NonNull AttributedOpEntry getDiscreteAccessAt(@IntRange(from = 0) int index) {
|
|
if (mDiscreteAccesses == null) {
|
|
throw new IndexOutOfBoundsException();
|
|
}
|
|
return mDiscreteAccesses.get(index);
|
|
}
|
|
|
|
/**
|
|
* Gets the number times the op was accessed (performed) in the foreground.
|
|
*
|
|
* @param flags The flags which are any combination of
|
|
* {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
|
|
* {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
|
|
* {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
|
|
* for any flag.
|
|
* @return The times the op was accessed in the foreground.
|
|
*
|
|
* @see #getBackgroundAccessCount(int)
|
|
* @see #getAccessCount(int, int, int)
|
|
*/
|
|
public long getForegroundAccessCount(@OpFlags int flags) {
|
|
return sumForFlagsInStates(mAccessCount, MAX_PRIORITY_UID_STATE,
|
|
resolveFirstUnrestrictedUidState(mOp), flags);
|
|
}
|
|
|
|
/**
|
|
* Gets the discrete events the op was accessed (performed) in the foreground.
|
|
*
|
|
* @param flags The flags which are any combination of
|
|
* {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
|
|
* {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
|
|
* {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
|
|
* for any flag.
|
|
* @return The list of discrete ops accessed in the foreground.
|
|
*
|
|
* @see #getBackgroundDiscreteAccesses(int)
|
|
* @see #getDiscreteAccesses(int, int, int)
|
|
*/
|
|
@NonNull
|
|
public List<AttributedOpEntry> getForegroundDiscreteAccesses(@OpFlags int flags) {
|
|
return listForFlagsInStates(mDiscreteAccesses, MAX_PRIORITY_UID_STATE,
|
|
resolveFirstUnrestrictedUidState(mOp), flags);
|
|
}
|
|
|
|
/**
|
|
* Gets the number times the op was accessed (performed) in the background.
|
|
*
|
|
* @param flags The flags which are any combination of
|
|
* {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
|
|
* {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
|
|
* {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
|
|
* for any flag.
|
|
* @return The times the op was accessed in the background.
|
|
*
|
|
* @see #getForegroundAccessCount(int)
|
|
* @see #getAccessCount(int, int, int)
|
|
*/
|
|
public long getBackgroundAccessCount(@OpFlags int flags) {
|
|
return sumForFlagsInStates(mAccessCount, resolveLastRestrictedUidState(mOp),
|
|
MIN_PRIORITY_UID_STATE, flags);
|
|
}
|
|
|
|
/**
|
|
* Gets the discrete events the op was accessed (performed) in the background.
|
|
*
|
|
* @param flags The flags which are any combination of
|
|
* {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
|
|
* {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
|
|
* {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
|
|
* for any flag.
|
|
* @return The list of discrete ops accessed in the background.
|
|
*
|
|
* @see #getForegroundDiscreteAccesses(int)
|
|
* @see #getDiscreteAccesses(int, int, int)
|
|
*/
|
|
@NonNull
|
|
public List<AttributedOpEntry> getBackgroundDiscreteAccesses(@OpFlags int flags) {
|
|
return listForFlagsInStates(mDiscreteAccesses, resolveLastRestrictedUidState(mOp),
|
|
MIN_PRIORITY_UID_STATE, flags);
|
|
}
|
|
|
|
/**
|
|
* Gets the number times the op was accessed (performed) for a
|
|
* range of uid states.
|
|
*
|
|
* @param fromUidState The UID state from which to query. Could be one of
|
|
* {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP},
|
|
* {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND},
|
|
* {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}.
|
|
* @param toUidState The UID state to which to query.
|
|
* @param flags The flags which are any combination of
|
|
* {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
|
|
* {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
|
|
* {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
|
|
* for any flag.
|
|
*
|
|
* @return The times the op was accessed for the given UID state.
|
|
*
|
|
* @see #getForegroundAccessCount(int)
|
|
* @see #getBackgroundAccessCount(int)
|
|
*/
|
|
public long getAccessCount(@UidState int fromUidState, @UidState int toUidState,
|
|
@OpFlags int flags) {
|
|
return sumForFlagsInStates(mAccessCount, fromUidState, toUidState, flags);
|
|
}
|
|
|
|
/**
|
|
* Gets the discrete events the op was accessed (performed) for a
|
|
* range of uid states.
|
|
*
|
|
* @param flags The flags which are any combination of
|
|
* {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
|
|
* {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
|
|
* {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
|
|
* for any flag.
|
|
* @return The discrete the op was accessed in the background.
|
|
*
|
|
* @see #getBackgroundDiscreteAccesses(int)
|
|
* @see #getForegroundDiscreteAccesses(int)
|
|
*/
|
|
@NonNull
|
|
public List<AttributedOpEntry> getDiscreteAccesses(@UidState int fromUidState,
|
|
@UidState int toUidState, @OpFlags int flags) {
|
|
return listForFlagsInStates(mDiscreteAccesses, fromUidState, toUidState, flags);
|
|
}
|
|
|
|
/**
|
|
* Gets the number times the op was rejected in the foreground.
|
|
*
|
|
* @param flags The flags which are any combination of
|
|
* {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
|
|
* {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
|
|
* {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
|
|
* for any flag.
|
|
* @return The times the op was rejected in the foreground.
|
|
*
|
|
* @see #getBackgroundRejectCount(int)
|
|
* @see #getRejectCount(int, int, int)
|
|
*/
|
|
public long getForegroundRejectCount(@OpFlags int flags) {
|
|
return sumForFlagsInStates(mRejectCount, MAX_PRIORITY_UID_STATE,
|
|
resolveFirstUnrestrictedUidState(mOp), flags);
|
|
}
|
|
|
|
/**
|
|
* Gets the number times the op was rejected in the background.
|
|
*
|
|
* @param flags The flags which are any combination of
|
|
* {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
|
|
* {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
|
|
* {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
|
|
* for any flag.
|
|
* @return The times the op was rejected in the background.
|
|
*
|
|
* @see #getForegroundRejectCount(int)
|
|
* @see #getRejectCount(int, int, int)
|
|
*/
|
|
public long getBackgroundRejectCount(@OpFlags int flags) {
|
|
return sumForFlagsInStates(mRejectCount, resolveLastRestrictedUidState(mOp),
|
|
MIN_PRIORITY_UID_STATE, flags);
|
|
}
|
|
|
|
/**
|
|
* Gets the number times the op was rejected for a given range of UID states.
|
|
*
|
|
* @param fromUidState The UID state from which to query. Could be one of
|
|
* {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP},
|
|
* {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND},
|
|
* {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}.
|
|
* @param toUidState The UID state to which to query.
|
|
* @param flags The flags which are any combination of
|
|
* {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
|
|
* {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
|
|
* {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
|
|
* for any flag.
|
|
*
|
|
* @return The times the op was rejected for the given UID state.
|
|
*
|
|
* @see #getForegroundRejectCount(int)
|
|
* @see #getBackgroundRejectCount(int)
|
|
*/
|
|
public long getRejectCount(@UidState int fromUidState, @UidState int toUidState,
|
|
@OpFlags int flags) {
|
|
return sumForFlagsInStates(mRejectCount, fromUidState, toUidState, flags);
|
|
}
|
|
|
|
/**
|
|
* Gets the total duration the app op was accessed (performed) in the foreground.
|
|
* The duration is in wall time.
|
|
*
|
|
* @param flags The flags which are any combination of
|
|
* {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
|
|
* {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
|
|
* {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
|
|
* for any flag.
|
|
* @return The total duration the app op was accessed in the foreground.
|
|
*
|
|
* @see #getBackgroundAccessDuration(int)
|
|
* @see #getAccessDuration(int, int, int)
|
|
*/
|
|
public long getForegroundAccessDuration(@OpFlags int flags) {
|
|
return sumForFlagsInStates(mAccessDuration, MAX_PRIORITY_UID_STATE,
|
|
resolveFirstUnrestrictedUidState(mOp), flags);
|
|
}
|
|
|
|
/**
|
|
* Gets the total duration the app op was accessed (performed) in the background.
|
|
* The duration is in wall time.
|
|
*
|
|
* @param flags The flags which are any combination of
|
|
* {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
|
|
* {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
|
|
* {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
|
|
* for any flag.
|
|
* @return The total duration the app op was accessed in the background.
|
|
*
|
|
* @see #getForegroundAccessDuration(int)
|
|
* @see #getAccessDuration(int, int, int)
|
|
*/
|
|
public long getBackgroundAccessDuration(@OpFlags int flags) {
|
|
return sumForFlagsInStates(mAccessDuration, resolveLastRestrictedUidState(mOp),
|
|
MIN_PRIORITY_UID_STATE, flags);
|
|
}
|
|
|
|
/**
|
|
* Gets the total duration the app op was accessed (performed) for a given
|
|
* range of UID states. The duration is in wall time.
|
|
*
|
|
* @param fromUidState The UID state from which to query. Could be one of
|
|
* {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP},
|
|
* {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND},
|
|
* {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}.
|
|
* @param toUidState The UID state from which to query.
|
|
* @param flags The flags which are any combination of
|
|
* {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
|
|
* {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
|
|
* {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
|
|
* for any flag.
|
|
*
|
|
* @return The total duration the app op was accessed for the given UID state.
|
|
*
|
|
* @see #getForegroundAccessDuration(int)
|
|
* @see #getBackgroundAccessDuration(int)
|
|
*/
|
|
public long getAccessDuration(@UidState int fromUidState, @UidState int toUidState,
|
|
@OpFlags int flags) {
|
|
return sumForFlagsInStates(mAccessDuration, fromUidState, toUidState, flags);
|
|
}
|
|
|
|
@Override
|
|
public int describeContents() {
|
|
return 0;
|
|
}
|
|
|
|
@Override
|
|
public void writeToParcel(Parcel parcel, int flags) {
|
|
parcel.writeInt(mOp);
|
|
writeLongSparseLongArrayToParcel(mAccessCount, parcel);
|
|
writeLongSparseLongArrayToParcel(mRejectCount, parcel);
|
|
writeLongSparseLongArrayToParcel(mAccessDuration, parcel);
|
|
writeDiscreteAccessArrayToParcel(mDiscreteAccesses, parcel, flags);
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(@Nullable Object obj) {
|
|
if (this == obj) {
|
|
return true;
|
|
}
|
|
if (obj == null || getClass() != obj.getClass()) {
|
|
return false;
|
|
}
|
|
final HistoricalOp other = (HistoricalOp) obj;
|
|
if (mOp != other.mOp) {
|
|
return false;
|
|
}
|
|
if (!equalsLongSparseLongArray(mAccessCount, other.mAccessCount)) {
|
|
return false;
|
|
}
|
|
if (!equalsLongSparseLongArray(mRejectCount, other.mRejectCount)) {
|
|
return false;
|
|
}
|
|
if (!equalsLongSparseLongArray(mAccessDuration, other.mAccessDuration)) {
|
|
return false;
|
|
}
|
|
return mDiscreteAccesses == null ? (other.mDiscreteAccesses == null ? true
|
|
: false) : mDiscreteAccesses.equals(other.mDiscreteAccesses);
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
int result = mOp;
|
|
result = 31 * result + Objects.hashCode(mAccessCount);
|
|
result = 31 * result + Objects.hashCode(mRejectCount);
|
|
result = 31 * result + Objects.hashCode(mAccessDuration);
|
|
result = 31 * result + Objects.hashCode(mDiscreteAccesses);
|
|
return result;
|
|
}
|
|
|
|
private void accept(@NonNull HistoricalOpsVisitor visitor) {
|
|
visitor.visitHistoricalOp(this);
|
|
}
|
|
|
|
private @NonNull LongSparseLongArray getOrCreateAccessCount() {
|
|
if (mAccessCount == null) {
|
|
mAccessCount = new LongSparseLongArray();
|
|
}
|
|
return mAccessCount;
|
|
}
|
|
|
|
private @NonNull LongSparseLongArray getOrCreateRejectCount() {
|
|
if (mRejectCount == null) {
|
|
mRejectCount = new LongSparseLongArray();
|
|
}
|
|
return mRejectCount;
|
|
}
|
|
|
|
private @NonNull LongSparseLongArray getOrCreateAccessDuration() {
|
|
if (mAccessDuration == null) {
|
|
mAccessDuration = new LongSparseLongArray();
|
|
}
|
|
return mAccessDuration;
|
|
}
|
|
|
|
private @NonNull List<AttributedOpEntry> getOrCreateDiscreteAccesses() {
|
|
if (mDiscreteAccesses == null) {
|
|
mDiscreteAccesses = new ArrayList<>();
|
|
}
|
|
return mDiscreteAccesses;
|
|
}
|
|
|
|
/**
|
|
* Multiplies the entries in the array with the passed in scale factor and
|
|
* rounds the result at up 0.5 boundary.
|
|
*
|
|
* @param data The data to scale.
|
|
* @param scaleFactor The scale factor.
|
|
*/
|
|
private static void scale(@NonNull LongSparseLongArray data, double scaleFactor) {
|
|
if (data != null) {
|
|
final int size = data.size();
|
|
for (int i = 0; i < size; i++) {
|
|
data.put(data.keyAt(i), (long) HistoricalOps.round(
|
|
(double) data.valueAt(i) * scaleFactor));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Merges two arrays while lazily acquiring the destination.
|
|
*
|
|
* @param thisSupplier The destination supplier.
|
|
* @param other The array to merge in.
|
|
*/
|
|
private static void merge(@NonNull Supplier<LongSparseLongArray> thisSupplier,
|
|
@Nullable LongSparseLongArray other) {
|
|
if (other != null) {
|
|
final int otherSize = other.size();
|
|
for (int i = 0; i < otherSize; i++) {
|
|
final LongSparseLongArray that = thisSupplier.get();
|
|
final long otherKey = other.keyAt(i);
|
|
final long otherValue = other.valueAt(i);
|
|
that.put(otherKey, that.get(otherKey) + otherValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @hide */
|
|
public @Nullable LongSparseArray<Object> collectKeys() {
|
|
LongSparseArray<Object> result = AppOpsManager.collectKeys(mAccessCount,
|
|
null /*result*/);
|
|
result = AppOpsManager.collectKeys(mRejectCount, result);
|
|
result = AppOpsManager.collectKeys(mAccessDuration, result);
|
|
return result;
|
|
}
|
|
|
|
public static final @android.annotation.NonNull Creator<HistoricalOp> CREATOR =
|
|
new Creator<HistoricalOp>() {
|
|
@Override
|
|
public @NonNull HistoricalOp createFromParcel(@NonNull Parcel source) {
|
|
return new HistoricalOp(source);
|
|
}
|
|
|
|
@Override
|
|
public @NonNull HistoricalOp[] newArray(int size) {
|
|
return new HistoricalOp[size];
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Computes the sum of the counts for the given flags in between the begin and
|
|
* end UID states.
|
|
*
|
|
* @param counts The data array.
|
|
* @param beginUidState The beginning UID state (inclusive).
|
|
* @param endUidState The end UID state (inclusive).
|
|
* @param flags The UID flags.
|
|
* @return The sum.
|
|
*/
|
|
private static long sumForFlagsInStates(@Nullable LongSparseLongArray counts,
|
|
@UidState int beginUidState, @UidState int endUidState, @OpFlags int flags) {
|
|
if (counts == null) {
|
|
return 0;
|
|
}
|
|
long sum = 0;
|
|
while (flags != 0) {
|
|
final int flag = 1 << Integer.numberOfTrailingZeros(flags);
|
|
flags &= ~flag;
|
|
for (int uidState : UID_STATES) {
|
|
if (uidState < beginUidState || uidState > endUidState) {
|
|
continue;
|
|
}
|
|
final long key = makeKey(uidState, flag);
|
|
sum += counts.get(key);
|
|
}
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
/**
|
|
* Returns list of events filtered by UidState and UID flags.
|
|
*
|
|
* @param accesses The events list.
|
|
* @param beginUidState The beginning UID state (inclusive).
|
|
* @param endUidState The end UID state (inclusive).
|
|
* @param flags The UID flags.
|
|
* @return filtered list of events.
|
|
*/
|
|
private static List<AttributedOpEntry> listForFlagsInStates(List<AttributedOpEntry> accesses,
|
|
@UidState int beginUidState, @UidState int endUidState, @OpFlags int flags) {
|
|
List<AttributedOpEntry> result = new ArrayList<>();
|
|
if (accesses == null) {
|
|
return result;
|
|
}
|
|
int nAccesses = accesses.size();
|
|
for (int i = 0; i < nAccesses; i++) {
|
|
AttributedOpEntry entry = accesses.get(i);
|
|
if (entry.getLastAccessTime(beginUidState, endUidState, flags) == -1) {
|
|
continue;
|
|
}
|
|
result.add(entry);
|
|
}
|
|
return deduplicateDiscreteEvents(result);
|
|
}
|
|
|
|
/**
|
|
* Callback for notification of changes to operation state.
|
|
*/
|
|
public interface OnOpChangedListener {
|
|
public void onOpChanged(String op, String packageName);
|
|
|
|
/**
|
|
* Implementations can override this method to add handling logic for AppOp changes.
|
|
*
|
|
* Normally, listeners to AppOp changes work in the same User Space as the App whose Op
|
|
* has changed. However, in some case listeners can have a single instance responsible for
|
|
* multiple users. (For ex single Media Provider instance in user 0 is responsible for both
|
|
* cloned and user 0 spaces). For handling such cases correctly, listeners need to be
|
|
* passed userId in addition to PackageName and Op.
|
|
|
|
* The default impl is to fallback onto {@link #onOpChanged(String, String)
|
|
*
|
|
* <p> Implement this method and not
|
|
* {@link #onOpChanged(String, String, int, String)} if
|
|
* callbacks are
|
|
* required only on op state changes for the default device
|
|
* {@link VirtualDeviceManager#PERSISTENT_DEVICE_ID_DEFAULT}.
|
|
*
|
|
* @param op The Op that changed.
|
|
* @param packageName Package of the app whose Op changed.
|
|
* @param userId User Space of the app whose Op changed.
|
|
* @hide
|
|
*/
|
|
default void onOpChanged(@NonNull String op, @NonNull String packageName, int userId) {
|
|
onOpChanged(op, packageName);
|
|
}
|
|
|
|
/**
|
|
* Similar to {@link #onOpChanged(String, String, int)} but includes the device for which
|
|
* the op mode has changed.
|
|
*
|
|
* <p> Implement this method if callbacks are required on all devices.
|
|
* If not implemented explicitly, the default implementation will notify for op changes
|
|
* on the default device {@link VirtualDeviceManager#PERSISTENT_DEVICE_ID_DEFAULT} only.
|
|
*
|
|
* <p> If implemented, {@link #onOpChanged(String, String, int)}
|
|
* will not be called automatically.
|
|
*
|
|
* @param op The Op that changed.
|
|
* @param packageName Package of the app whose Op changed.
|
|
* @param userId User id of the app whose Op changed.
|
|
* @param persistentDeviceId persistent device id whose Op changed.
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
|
|
default void onOpChanged(@NonNull String op, @NonNull String packageName, int userId,
|
|
@NonNull String persistentDeviceId) {
|
|
if (Objects.equals(persistentDeviceId,
|
|
VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT)) {
|
|
onOpChanged(op, packageName, userId);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Callback for notification of changes to operation active state.
|
|
*/
|
|
public interface OnOpActiveChangedListener {
|
|
/**
|
|
* Called when the active state of an app-op changes.
|
|
*
|
|
* @param op The operation that changed.
|
|
* @param packageName The package performing the operation.
|
|
* @param active Whether the operation became active or inactive.
|
|
*/
|
|
void onOpActiveChanged(@NonNull String op, int uid, @NonNull String packageName,
|
|
boolean active);
|
|
|
|
/**
|
|
* Called when the active state of an app-op changes.
|
|
*
|
|
* <p> Implement this method and not
|
|
* {@link #onOpActiveChanged(String, int, String, String, int, boolean, int, int)} if
|
|
* callbacks are
|
|
* required only on op state changes for the default device
|
|
* {@link Context#DEVICE_ID_DEFAULT}.
|
|
*
|
|
* @param op The operation that changed.
|
|
* @param uid The UID performing the operation.
|
|
* @param packageName The package performing the operation.
|
|
* @param attributionTag The operation's attribution tag.
|
|
* @param active Whether the operation became active or inactive.
|
|
* @param attributionFlags the attribution flags for this operation.
|
|
* @param attributionChainId the unique id of the attribution chain this op is a part of.
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
default void onOpActiveChanged(@NonNull String op, int uid, @NonNull String packageName,
|
|
@Nullable String attributionTag, boolean active, @AttributionFlags
|
|
int attributionFlags, int attributionChainId) {
|
|
onOpActiveChanged(op, uid, packageName, active);
|
|
}
|
|
|
|
/**
|
|
* Similar to {@link #onOpActiveChanged(String, int, String, String, boolean, int, int)},
|
|
* but also includes the virtual device id of the op is now active or inactive.
|
|
*
|
|
* <p> Implement this method if callbacks are required on all devices.
|
|
* If not implemented explicitly, the default implementation will notify for op state
|
|
* changes on the default device {@link Context#DEVICE_ID_DEFAULT} only.
|
|
*
|
|
* <p> If implemented,
|
|
* {@link #onOpActiveChanged(String, int, String, String, boolean, int, int)}
|
|
* will not be called automatically.
|
|
*
|
|
* @param op The operation that changed.
|
|
* @param uid The UID performing the operation.
|
|
* @param packageName The package performing the operation.
|
|
* @param attributionTag The operation's attribution tag.
|
|
* @param virtualDeviceId the virtual device id whose operation has changed
|
|
* @param active Whether the operation became active or inactive.
|
|
* @param attributionFlags the attribution flags for this operation.
|
|
* @param attributionChainId the unique id of the attribution chain this op is a part of.
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
|
|
default void onOpActiveChanged(@NonNull String op, int uid, @NonNull String packageName,
|
|
@Nullable String attributionTag, int virtualDeviceId, boolean active,
|
|
@AttributionFlags int attributionFlags, int attributionChainId) {
|
|
if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) {
|
|
onOpActiveChanged(op, uid, packageName, attributionTag, active, attributionFlags,
|
|
attributionChainId);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Callback for notification of an app-op being noted.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public interface OnOpNotedListener {
|
|
/**
|
|
* Called when an app-op is noted.
|
|
*
|
|
* <p> Implement this method and not
|
|
* {@link #onOpNoted(String, int, String, String, int, int, int)} if callbacks are
|
|
* required only on op notes for the default device {@link Context#DEVICE_ID_DEFAULT}.
|
|
*
|
|
* @param op The operation that was noted.
|
|
* @param uid The UID performing the operation.
|
|
* @param packageName The package performing the operation.
|
|
* @param attributionTag The attribution tag performing the operation.
|
|
* @param flags The flags of this op
|
|
* @param result The result of the note.
|
|
*/
|
|
void onOpNoted(@NonNull String op, int uid, @NonNull String packageName,
|
|
@Nullable String attributionTag, @OpFlags int flags, @Mode int result);
|
|
|
|
/**
|
|
* Similar to {@link #onOpNoted(String, int, String, String, int, int, int)},
|
|
* but also includes the virtual device id of the op is now active or inactive.
|
|
*
|
|
* <p> Implement this method if callbacks are required for op notes on all devices.
|
|
* If not implemented explicitly, the default implementation will notify for the
|
|
* default device {@link Context#DEVICE_ID_DEFAULT} only.
|
|
*
|
|
* <p> If implemented, {@link #onOpNoted(String, int, String, String, int, int)}
|
|
* will not be called automatically.
|
|
*
|
|
* @param op The operation that was noted.
|
|
* @param uid The UID performing the operation.
|
|
* @param packageName The package performing the operation.
|
|
* @param attributionTag The attribution tag performing the operation.
|
|
* @param virtualDeviceId the device that noted the operation
|
|
* @param flags The flags of this op
|
|
* @param result The result of the note.
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
|
|
default void onOpNoted(@NonNull String op, int uid, @NonNull String packageName,
|
|
@Nullable String attributionTag, int virtualDeviceId, @OpFlags int flags,
|
|
@Mode int result) {
|
|
if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) {
|
|
onOpNoted(op, uid, packageName, attributionTag, flags, result);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Callback for notification of an app-op being noted to be used within platform code.
|
|
*
|
|
* This allows being notified using raw op codes instead of string op names.
|
|
*
|
|
* @hide
|
|
*/
|
|
public interface OnOpNotedInternalListener extends OnOpNotedListener {
|
|
/**
|
|
* Called when an app-op is noted.
|
|
*
|
|
* @param code The code of the operation that was noted.
|
|
* @param uid The UID performing the operation.
|
|
* @param packageName The package performing the operation.
|
|
* @param attributionTag The attribution tag performing the operation.
|
|
* @param flags The flags of this op
|
|
* @param result The result of the note.
|
|
*/
|
|
void onOpNoted(int code, int uid, @NonNull String packageName,
|
|
@Nullable String attributionTag, @OpFlags int flags, @Mode int result);
|
|
|
|
@Override
|
|
default void onOpNoted(@NonNull String op, int uid, @NonNull String packageName,
|
|
@Nullable String attributionTag, @OpFlags int flags, @Mode int result) {
|
|
onOpNoted(strOpToOp(op), uid, packageName, attributionTag, flags, result);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Callback for notification of changes to operation state.
|
|
* This allows you to see the raw op codes instead of strings.
|
|
* @hide
|
|
*/
|
|
public static class OnOpChangedInternalListener implements OnOpChangedListener {
|
|
public void onOpChanged(String op, String packageName) { }
|
|
public void onOpChanged(int op, String packageName) { }
|
|
public void onOpChanged(int op, String packageName, String persistentDeviceId) {
|
|
onOpChanged(op, packageName);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Callback for notification of changes to operation state.
|
|
* This allows you to see the raw op codes instead of strings.
|
|
* @hide
|
|
*/
|
|
public interface OnOpActiveChangedInternalListener extends OnOpActiveChangedListener {
|
|
default void onOpActiveChanged(String op, int uid, String packageName, boolean active) { }
|
|
default void onOpActiveChanged(int op, int uid, String packageName, boolean active) { }
|
|
default void onOpActiveChanged(int op, int uid, String packageName, int virtualDeviceId,
|
|
boolean active) { }
|
|
}
|
|
|
|
/**
|
|
* Callback for notification of an op being started.
|
|
*
|
|
* @hide
|
|
*/
|
|
public interface OnOpStartedListener {
|
|
|
|
/**
|
|
* Represents a start operation that was unsuccessful
|
|
* @hide
|
|
*/
|
|
public int START_TYPE_FAILED = 0;
|
|
|
|
/**
|
|
* Represents a successful start operation
|
|
* @hide
|
|
*/
|
|
public int START_TYPE_STARTED = 1;
|
|
|
|
/**
|
|
* Represents an operation where a restricted operation became unrestricted, and resumed.
|
|
* @hide
|
|
*/
|
|
public int START_TYPE_RESUMED = 2;
|
|
|
|
/** @hide */
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@IntDef(flag = true, prefix = { "TYPE_" }, value = {
|
|
START_TYPE_FAILED,
|
|
START_TYPE_STARTED,
|
|
START_TYPE_RESUMED
|
|
})
|
|
public @interface StartedType {}
|
|
|
|
/**
|
|
* Called when an op was started.
|
|
*
|
|
* Note: This is only for op starts. It is not called when an op is noted or stopped.
|
|
* @param op The op code.
|
|
* @param uid The UID performing the operation.
|
|
* @param packageName The package performing the operation.
|
|
* @param attributionTag The attribution tag performing the operation.
|
|
* @param flags The flags of this op.
|
|
* @param result The result of the start.
|
|
*/
|
|
void onOpStarted(int op, int uid, String packageName, String attributionTag,
|
|
@OpFlags int flags, @Mode int result);
|
|
|
|
/**
|
|
* Called when an op was started.
|
|
*
|
|
* <p> Implement this method and not
|
|
* {@link #onOpStarted(int, int, String, String, int, int, int, int, int, int)} if
|
|
* callbacks are
|
|
* required only on op starts for the default device
|
|
* {@link Context#DEVICE_ID_DEFAULT}.
|
|
*
|
|
* Note: This is only for op starts. It is not called when an op is noted or stopped.
|
|
* By default, unless this method is overridden, no code will be executed for resume
|
|
* events.
|
|
* @param op The op code.
|
|
* @param uid The UID performing the operation.
|
|
* @param packageName The package performing the operation.
|
|
* @param attributionTag The attribution tag performing the operation.
|
|
* @param flags The flags of this op.
|
|
* @param result The result of the start.
|
|
* @param startType The start type of this start event. Either failed, resumed, or started.
|
|
* @param attributionFlags The location of this started op in an attribution chain.
|
|
* @param attributionChainId The ID of the attribution chain of this op, if it is in one.
|
|
*/
|
|
default void onOpStarted(int op, int uid, String packageName, String attributionTag,
|
|
@OpFlags int flags, @Mode int result, @StartedType int startType,
|
|
@AttributionFlags int attributionFlags, int attributionChainId) {
|
|
if (startType != START_TYPE_RESUMED) {
|
|
onOpStarted(op, uid, packageName, attributionTag, flags, result);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Similar to {@link #onOpStarted(int, int, String, String, int, int)},
|
|
* but also includes the virtual device id that started the op.
|
|
*
|
|
* <p> Implement this method if callbacks are required on all devices.
|
|
* If not implemented explicitly, the default implementation will notify for op starts on
|
|
* the default device {@link Context#DEVICE_ID_DEFAULT} only.
|
|
*
|
|
* <p> If implemented, {@link #onOpStarted(int, int, String, String, int, int)}
|
|
* will not be called automatically.
|
|
*
|
|
* @param op The op code.
|
|
* @param uid The UID performing the operation.
|
|
* @param packageName The package performing the operation.
|
|
* @param attributionTag The attribution tag performing the operation.
|
|
* @param virtualDeviceId the device that started the operation
|
|
* @param flags The flags of this op.
|
|
* @param result The result of the start.
|
|
* @param startType The start type of this start event. Either failed, resumed, or started.
|
|
* @param attributionFlags The location of this started op in an attribution chain.
|
|
*/
|
|
default void onOpStarted(int op, int uid, @NonNull String packageName,
|
|
@Nullable String attributionTag, int virtualDeviceId, @OpFlags int flags,
|
|
@Mode int result, @StartedType int startType,
|
|
@AttributionFlags int attributionFlags, int attributionChainId) {
|
|
if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) {
|
|
onOpStarted(op, uid, packageName, attributionTag, flags, result, startType,
|
|
attributionFlags, attributionChainId);
|
|
}
|
|
}
|
|
}
|
|
|
|
AppOpsManager(Context context, IAppOpsService service) {
|
|
mContext = context;
|
|
mService = service;
|
|
|
|
if (mContext != null) {
|
|
final PackageManager pm = mContext.getPackageManager();
|
|
try {
|
|
if (Build.IS_ENG
|
|
&& pm != null
|
|
&& pm.checkPermission(
|
|
Manifest.permission.READ_DEVICE_CONFIG,
|
|
mContext.getPackageName())
|
|
== PackageManager.PERMISSION_GRANTED) {
|
|
DeviceConfig.addOnPropertiesChangedListener(
|
|
DeviceConfig.NAMESPACE_PRIVACY,
|
|
mContext.getMainExecutor(),
|
|
properties -> {
|
|
if (properties.getKeyset().contains(FULL_LOG)) {
|
|
sFullLog = properties.getBoolean(FULL_LOG, false);
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
} catch (Exception e) {
|
|
// This manager was made before DeviceConfig is ready, so it's a low-level
|
|
// system app. We likely don't care about its logs.
|
|
}
|
|
}
|
|
sFullLog = false;
|
|
}
|
|
|
|
/**
|
|
* Retrieve current operation state for all applications.
|
|
*
|
|
* The mode of the ops returned are set for the package but may not reflect their effective
|
|
* state due to UID policy or because it's controlled by a different global op.
|
|
*
|
|
* Use {@link #unsafeCheckOp(String, int, String)}} or
|
|
* {@link #noteOp(String, int, String, String, String)} if the effective mode is needed.
|
|
*
|
|
* @param ops The set of operations you are interested in, or null if you want all of them.
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
@RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS)
|
|
public @NonNull List<AppOpsManager.PackageOps> getPackagesForOps(@Nullable String[] ops) {
|
|
final int[] opCodes;
|
|
if (ops != null) {
|
|
final int opCount = ops.length;
|
|
opCodes = new int[opCount];
|
|
for (int i = 0; i < opCount; i++) {
|
|
opCodes[i] = sOpStrToOp.get(ops[i]);
|
|
}
|
|
} else {
|
|
opCodes = null;
|
|
}
|
|
final List<AppOpsManager.PackageOps> result = getPackagesForOps(opCodes);
|
|
return (result != null) ? result : Collections.emptyList();
|
|
}
|
|
|
|
/**
|
|
* Retrieve current operation state for all applications for a device.
|
|
*
|
|
* The mode of the ops returned are set for the package but may not reflect their effective
|
|
* state due to UID policy or because it's controlled by a different global op.
|
|
*
|
|
* Use {@link #unsafeCheckOp(String, int, String)}} or
|
|
* {@link #noteOp(String, int, String, String, String)} if the effective mode is needed.
|
|
*
|
|
* @param ops The set of operations you are interested in, or null if you want all of them.
|
|
* @param persistentDeviceId The device that the ops are attributed to.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
@FlaggedApi(android.permission.flags.Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
|
|
@RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS)
|
|
public @NonNull List<AppOpsManager.PackageOps> getPackagesForOps(@Nullable String[] ops,
|
|
@NonNull String persistentDeviceId) {
|
|
final int[] opCodes;
|
|
if (ops != null) {
|
|
final int opCount = ops.length;
|
|
opCodes = new int[opCount];
|
|
for (int i = 0; i < opCount; i++) {
|
|
opCodes[i] = sOpStrToOp.get(ops[i]);
|
|
}
|
|
} else {
|
|
opCodes = null;
|
|
}
|
|
final List<AppOpsManager.PackageOps> result;
|
|
try {
|
|
result = mService.getPackagesForOpsForDevice(opCodes, persistentDeviceId);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
return (result != null) ? result : Collections.emptyList();
|
|
}
|
|
|
|
/**
|
|
* Retrieve current operation state for all applications.
|
|
*
|
|
* The mode of the ops returned are set for the package but may not reflect their effective
|
|
* state due to UID policy or because it's controlled by a different global op.
|
|
*
|
|
* Use {@link #unsafeCheckOp(String, int, String)}} or
|
|
* {@link #noteOp(String, int, String, String, String)} if the effective mode is needed.
|
|
*
|
|
* @param ops The set of operations you are interested in, or null if you want all of them.
|
|
* @hide
|
|
*/
|
|
@RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS)
|
|
@UnsupportedAppUsage
|
|
public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) {
|
|
try {
|
|
return mService.getPackagesForOpsForDevice(ops,
|
|
VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieve current operation state for one application.
|
|
*
|
|
* The mode of the ops returned are set for the package but may not reflect their effective
|
|
* state due to UID policy or because it's controlled by a different global op.
|
|
*
|
|
* Use {@link #unsafeCheckOp(String, int, String)}} or
|
|
* {@link #noteOp(String, int, String, String, String)} if the effective mode is needed.
|
|
*
|
|
* @param uid The uid of the application of interest.
|
|
* @param packageName The name of the application of interest.
|
|
* @param ops The set of operations you are interested in, or null if you want all of them.
|
|
*
|
|
* @deprecated The int op codes are not stable and you should use the string based op
|
|
* names which are stable and namespaced. Use
|
|
* {@link #getOpsForPackage(int, String, String...)})}.
|
|
*
|
|
* @hide
|
|
* @removed
|
|
*/
|
|
@Deprecated
|
|
@SystemApi
|
|
@RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS)
|
|
public @NonNull List<PackageOps> getOpsForPackage(int uid, @NonNull String packageName,
|
|
@Nullable int[] ops) {
|
|
try {
|
|
return mService.getOpsForPackage(uid, packageName, ops);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieve current operation state for one application. The UID and the
|
|
* package must match.
|
|
*
|
|
* The mode of the ops returned are set for the package but may not reflect their effective
|
|
* state due to UID policy or because it's controlled by a different global op.
|
|
*
|
|
* Use {@link #unsafeCheckOp(String, int, String)}} or
|
|
* {@link #noteOp(String, int, String, String, String)} if the effective mode is needed.
|
|
*
|
|
* @param uid The uid of the application of interest.
|
|
* @param packageName The name of the application of interest.
|
|
* @param ops The set of operations you are interested in, or null if you want all of them.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
@RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS)
|
|
public @NonNull List<AppOpsManager.PackageOps> getOpsForPackage(int uid,
|
|
@NonNull String packageName, @Nullable String... ops) {
|
|
int[] opCodes = null;
|
|
if (ops != null) {
|
|
opCodes = new int[ops.length];
|
|
for (int i = 0; i < ops.length; i++) {
|
|
opCodes[i] = strOpToOp(ops[i]);
|
|
}
|
|
}
|
|
try {
|
|
final List<PackageOps> result = mService.getOpsForPackage(uid, packageName, opCodes);
|
|
if (result == null) {
|
|
return Collections.emptyList();
|
|
}
|
|
return result;
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieve historical app op stats for a period.
|
|
*
|
|
* @param request A request object describing the data being queried for.
|
|
* @param executor Executor on which to run the callback. If <code>null</code>
|
|
* the callback is executed on the default executor running on the main thread.
|
|
* @param callback Callback on which to deliver the result.
|
|
*
|
|
* @throws IllegalArgumentException If any of the argument contracts is violated.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
@RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS)
|
|
public void getHistoricalOps(@NonNull HistoricalOpsRequest request,
|
|
@NonNull Executor executor, @NonNull Consumer<HistoricalOps> callback) {
|
|
Objects.requireNonNull(executor, "executor cannot be null");
|
|
Objects.requireNonNull(callback, "callback cannot be null");
|
|
try {
|
|
mService.getHistoricalOps(request.mUid, request.mPackageName, request.mAttributionTag,
|
|
request.mOpNames, request.mHistoryFlags, request.mFilter,
|
|
request.mBeginTimeMillis, request.mEndTimeMillis, request.mFlags,
|
|
new RemoteCallback((result) -> {
|
|
final HistoricalOps ops = result.getParcelable(KEY_HISTORICAL_OPS, android.app.AppOpsManager.HistoricalOps.class);
|
|
final long identity = Binder.clearCallingIdentity();
|
|
try {
|
|
executor.execute(() -> callback.accept(ops));
|
|
} finally {
|
|
Binder.restoreCallingIdentity(identity);
|
|
}
|
|
}));
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieve historical app op stats for a period.
|
|
* <p>
|
|
* This method queries only the on disk state and the returned ops are raw,
|
|
* which is their times are relative to the history start as opposed to the
|
|
* epoch start.
|
|
*
|
|
* @param request A request object describing the data being queried for.
|
|
* @param executor Executor on which to run the callback. If <code>null</code>
|
|
* the callback is executed on the default executor running on the main thread.
|
|
* @param callback Callback on which to deliver the result.
|
|
*
|
|
* @throws IllegalArgumentException If any of the argument contracts is violated.
|
|
*
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
@RequiresPermission(Manifest.permission.MANAGE_APPOPS)
|
|
public void getHistoricalOpsFromDiskRaw(@NonNull HistoricalOpsRequest request,
|
|
@Nullable Executor executor, @NonNull Consumer<HistoricalOps> callback) {
|
|
Objects.requireNonNull(executor, "executor cannot be null");
|
|
Objects.requireNonNull(callback, "callback cannot be null");
|
|
try {
|
|
mService.getHistoricalOpsFromDiskRaw(request.mUid, request.mPackageName,
|
|
request.mAttributionTag, request.mOpNames, request.mHistoryFlags,
|
|
request.mFilter, request.mBeginTimeMillis, request.mEndTimeMillis,
|
|
request.mFlags, new RemoteCallback((result) -> {
|
|
final HistoricalOps ops = result.getParcelable(KEY_HISTORICAL_OPS, android.app.AppOpsManager.HistoricalOps.class);
|
|
final long identity = Binder.clearCallingIdentity();
|
|
try {
|
|
executor.execute(() -> callback.accept(ops));
|
|
} finally {
|
|
Binder.restoreCallingIdentity(identity);
|
|
}
|
|
}));
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reloads the non historical state to allow testing the read/write path.
|
|
*
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
@RequiresPermission(Manifest.permission.MANAGE_APPOPS)
|
|
public void reloadNonHistoricalState() {
|
|
try {
|
|
mService.reloadNonHistoricalState();
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets given app op in the specified mode for app ops in the UID.
|
|
* This applies to all apps currently in the UID or installed in
|
|
* this UID in the future.
|
|
*
|
|
* @param code The app op.
|
|
* @param uid The UID for which to set the app.
|
|
* @param mode The app op mode to set.
|
|
* @hide
|
|
*/
|
|
@RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
|
|
public void setUidMode(int code, int uid, @Mode int mode) {
|
|
try {
|
|
// TODO(b/302609140): Remove extra logging after this issue is diagnosed.
|
|
if (code == OP_BLUETOOTH_CONNECT) {
|
|
Log.i(DEBUG_LOGGING_TAG,
|
|
"setUidMode called for OP_BLUETOOTH_CONNECT with mode: " + mode
|
|
+ " for uid: " + uid + " calling uid: " + Binder.getCallingUid()
|
|
+ " trace: "
|
|
+ Arrays.toString(Thread.currentThread().getStackTrace()));
|
|
}
|
|
mService.setUidMode(code, uid, mode);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets given app op in the specified mode for app ops in the UID.
|
|
* This applies to all apps currently in the UID or installed in
|
|
* this UID in the future.
|
|
*
|
|
* @param appOp The app op.
|
|
* @param uid The UID for which to set the app.
|
|
* @param mode The app op mode to set.
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
@RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
|
|
public void setUidMode(@NonNull String appOp, int uid, @Mode int mode) {
|
|
try {
|
|
// TODO(b/302609140): Remove extra logging after this issue is diagnosed.
|
|
if (appOp.equals(OPSTR_BLUETOOTH_CONNECT)) {
|
|
Log.i(DEBUG_LOGGING_TAG,
|
|
"setUidMode called for OPSTR_BLUETOOTH_CONNECT with mode: " + mode
|
|
+ " for uid: " + uid + " calling uid: " + Binder.getCallingUid()
|
|
+ " trace: "
|
|
+ Arrays.toString(Thread.currentThread().getStackTrace()));
|
|
}
|
|
|
|
mService.setUidMode(AppOpsManager.strOpToOp(appOp), uid, mode);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/** @hide */
|
|
public void setUserRestriction(int code, boolean restricted, IBinder token) {
|
|
setUserRestriction(code, restricted, token, null);
|
|
}
|
|
|
|
/**
|
|
* An empty array of attribution tags means exclude all tags under that package.
|
|
* @hide
|
|
*/
|
|
public void setUserRestriction(int code, boolean restricted, IBinder token,
|
|
@Nullable PackageTagsList excludedPackageTags) {
|
|
setUserRestrictionForUser(code, restricted, token, excludedPackageTags,
|
|
mContext.getUserId());
|
|
}
|
|
|
|
/**
|
|
* An empty array of attribution tags means exclude all tags under that package.
|
|
* @hide
|
|
*/
|
|
public void setUserRestrictionForUser(int code, boolean restricted, IBinder token,
|
|
@Nullable PackageTagsList excludedPackageTags, int userId) {
|
|
try {
|
|
mService.setUserRestriction(code, restricted, token, userId, excludedPackageTags);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/** @hide */
|
|
@UnsupportedAppUsage
|
|
@TestApi
|
|
@RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
|
|
public void setMode(int code, int uid, String packageName, @Mode int mode) {
|
|
try {
|
|
// TODO(b/302609140): Remove extra logging after this issue is diagnosed.
|
|
if (code == OP_BLUETOOTH_CONNECT) {
|
|
Log.i(DEBUG_LOGGING_TAG,
|
|
"setMode called for OPSTR_BLUETOOTH_CONNECT with mode: " + mode
|
|
+ " for uid: " + uid + " calling uid: " + Binder.getCallingUid()
|
|
+ " trace: "
|
|
+ Arrays.toString(Thread.currentThread().getStackTrace()));
|
|
}
|
|
mService.setMode(code, uid, packageName, mode);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Change the operating mode for the given op in the given app package. You must pass
|
|
* in both the uid and name of the application whose mode is being modified; if these
|
|
* do not match, the modification will not be applied.
|
|
*
|
|
* @param op The operation to modify. One of the OPSTR_* constants.
|
|
* @param uid The user id of the application whose mode will be changed.
|
|
* @param packageName The name of the application package name whose mode will
|
|
* be changed.
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
@RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
|
|
public void setMode(@NonNull String op, int uid, @Nullable String packageName,
|
|
@Mode int mode) {
|
|
try {
|
|
// TODO(b/302609140): Remove extra logging after this issue is diagnosed.
|
|
if (op.equals(OPSTR_BLUETOOTH_CONNECT)) {
|
|
Log.i(DEBUG_LOGGING_TAG,
|
|
"setMode called for OPSTR_BLUETOOTH_CONNECT with mode: " + mode
|
|
+ " for uid: " + uid + " calling uid: " + Binder.getCallingUid()
|
|
+ " trace: "
|
|
+ Arrays.toString(Thread.currentThread().getStackTrace()));
|
|
}
|
|
mService.setMode(strOpToOp(op), uid, packageName, mode);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set a non-persisted restriction on an audio operation at a stream-level.
|
|
* Restrictions are temporary additional constraints imposed on top of the persisted rules
|
|
* defined by {@link #setMode}.
|
|
*
|
|
* @param code The operation to restrict.
|
|
* @param usage The {@link android.media.AudioAttributes} usage value.
|
|
* @param mode The restriction mode (MODE_IGNORED,MODE_ERRORED) or MODE_ALLOWED to unrestrict.
|
|
* @param exceptionPackages Optional list of packages to exclude from the restriction.
|
|
* @hide
|
|
*/
|
|
@RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
|
|
@UnsupportedAppUsage
|
|
public void setRestriction(int code, @AttributeUsage int usage, @Mode int mode,
|
|
String[] exceptionPackages) {
|
|
try {
|
|
final int uid = Binder.getCallingUid();
|
|
mService.setAudioRestriction(code, usage, uid, mode, exceptionPackages);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/** @hide */
|
|
@RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
|
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
|
public void resetAllModes() {
|
|
try {
|
|
mService.resetAllModes(mContext.getUserId(), null);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the app-op name associated with a given permission.
|
|
*
|
|
* <p>The app-op name is one of the public constants defined
|
|
* in this class such as {@link #OPSTR_COARSE_LOCATION}.
|
|
* This API is intended to be used for mapping runtime
|
|
* permissions to the corresponding app-op.
|
|
*
|
|
* @param permission The permission.
|
|
* @return The app-op associated with the permission or {@code null}.
|
|
*/
|
|
public static @Nullable String permissionToOp(@NonNull String permission) {
|
|
final Integer opCode = sPermToOp.get(permission);
|
|
if (opCode != null) {
|
|
return sAppOpInfos[opCode].name;
|
|
}
|
|
if (HealthConnectManager.isHealthPermission(ActivityThread.currentApplication(),
|
|
permission)) {
|
|
return sAppOpInfos[OP_READ_WRITE_HEALTH_DATA].name;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Resolves special UID's pakcages such as root, shell, media, etc.
|
|
*
|
|
* @param uid The uid to resolve.
|
|
* @param packageName Optional package. If caller system and null returns "android"
|
|
* @return The resolved package name.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static @Nullable String resolvePackageName(int uid, @Nullable String packageName) {
|
|
if (uid == Process.ROOT_UID) {
|
|
return "root";
|
|
} else if (uid == Process.SHELL_UID) {
|
|
return "com.android.shell";
|
|
} else if (uid == Process.MEDIA_UID) {
|
|
return "media";
|
|
} else if (uid == Process.AUDIOSERVER_UID) {
|
|
return "audioserver";
|
|
} else if (uid == Process.CAMERASERVER_UID) {
|
|
return "cameraserver";
|
|
} else if (uid == Process.SYSTEM_UID && packageName == null) {
|
|
return "android";
|
|
}
|
|
return packageName;
|
|
}
|
|
|
|
/**
|
|
* Monitor for changes to the operating mode for the given op in the given app package.
|
|
* You can watch op changes only for your UID.
|
|
*
|
|
* @param op The operation to monitor, one of OPSTR_*.
|
|
* @param packageName The name of the application to monitor.
|
|
* @param callback Where to report changes.
|
|
*/
|
|
public void startWatchingMode(@NonNull String op, @Nullable String packageName,
|
|
@NonNull final OnOpChangedListener callback) {
|
|
startWatchingMode(strOpToOp(op), packageName, callback);
|
|
}
|
|
|
|
/**
|
|
* Monitor for changes to the operating mode for the given op in the given app package.
|
|
* You can watch op changes only for your UID.
|
|
*
|
|
* @param op The operation to monitor, one of OPSTR_*.
|
|
* @param packageName The name of the application to monitor.
|
|
* @param flags Option flags: any combination of {@link #WATCH_FOREGROUND_CHANGES} or 0.
|
|
* @param callback Where to report changes.
|
|
*/
|
|
public void startWatchingMode(@NonNull String op, @Nullable String packageName, int flags,
|
|
@NonNull final OnOpChangedListener callback) {
|
|
startWatchingMode(strOpToOp(op), packageName, flags, callback);
|
|
}
|
|
|
|
/**
|
|
* Monitor for changes to the operating mode for the given op in the given app package.
|
|
*
|
|
* <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission
|
|
* you can watch changes only for your UID.
|
|
*
|
|
* @param op The operation to monitor, one of OP_*.
|
|
* @param packageName The name of the application to monitor.
|
|
* @param callback Where to report changes.
|
|
* @hide
|
|
*/
|
|
@RequiresPermission(value=android.Manifest.permission.WATCH_APPOPS, conditional=true)
|
|
public void startWatchingMode(int op, String packageName, final OnOpChangedListener callback) {
|
|
startWatchingMode(op, packageName, 0, callback);
|
|
}
|
|
|
|
/**
|
|
* Monitor for changes to the operating mode for the given op in the given app package.
|
|
*
|
|
* <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission
|
|
* you can watch changes only for your UID.
|
|
*
|
|
* @param op The operation to monitor, one of OP_*.
|
|
* @param packageName The name of the application to monitor.
|
|
* @param flags Option flags: any combination of {@link #WATCH_FOREGROUND_CHANGES} or 0.
|
|
* @param callback Where to report changes.
|
|
* @hide
|
|
*/
|
|
@RequiresPermission(value=android.Manifest.permission.WATCH_APPOPS, conditional=true)
|
|
public void startWatchingMode(int op, String packageName, int flags,
|
|
final OnOpChangedListener callback) {
|
|
synchronized (mModeWatchers) {
|
|
IAppOpsCallback cb = mModeWatchers.get(callback);
|
|
if (cb == null) {
|
|
cb = new IAppOpsCallback.Stub() {
|
|
public void opChanged(int op, int uid, String packageName,
|
|
String persistentDeviceId) {
|
|
if (Flags.deviceAwarePermissionApisEnabled()) {
|
|
if (callback instanceof OnOpChangedInternalListener) {
|
|
((OnOpChangedInternalListener)callback).onOpChanged(op, packageName,
|
|
persistentDeviceId);
|
|
}
|
|
if (sAppOpInfos[op].name != null) {
|
|
callback.onOpChanged(sAppOpInfos[op].name, packageName,
|
|
UserHandle.getUserId(uid), persistentDeviceId);
|
|
}
|
|
} else {
|
|
if (callback instanceof OnOpChangedInternalListener) {
|
|
((OnOpChangedInternalListener) callback).onOpChanged(op,
|
|
packageName);
|
|
}
|
|
if (sAppOpInfos[op].name != null) {
|
|
callback.onOpChanged(sAppOpInfos[op].name, packageName,
|
|
UserHandle.getUserId(uid));
|
|
}
|
|
}
|
|
}
|
|
};
|
|
mModeWatchers.put(callback, cb);
|
|
}
|
|
|
|
// See CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE
|
|
if (!Compatibility.isChangeEnabled(
|
|
CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE)) {
|
|
flags |= CALL_BACK_ON_SWITCHED_OP;
|
|
}
|
|
|
|
try {
|
|
mService.startWatchingModeWithFlags(op, packageName, flags, cb);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stop monitoring that was previously started with {@link #startWatchingMode}. All
|
|
* monitoring associated with this callback will be removed.
|
|
*/
|
|
public void stopWatchingMode(@NonNull OnOpChangedListener callback) {
|
|
synchronized (mModeWatchers) {
|
|
IAppOpsCallback cb = mModeWatchers.remove(callback);
|
|
if (cb != null) {
|
|
try {
|
|
mService.stopWatchingMode(cb);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** {@hide} */
|
|
@Deprecated
|
|
public void startWatchingActive(@NonNull int[] ops,
|
|
@NonNull OnOpActiveChangedListener callback) {
|
|
final String[] strOps = new String[ops.length];
|
|
for (int i = 0; i < ops.length; i++) {
|
|
strOps[i] = opToPublicName(ops[i]);
|
|
}
|
|
startWatchingActive(strOps, mContext.getMainExecutor(), callback);
|
|
}
|
|
|
|
/**
|
|
* Start watching for changes to the active state of app-ops. An app-op may be
|
|
* long running and it has a clear start and stop delimiters. If an op is being
|
|
* started or stopped by any package you will get a callback. To change the
|
|
* watched ops for a registered callback you need to unregister and register it
|
|
* again.
|
|
*
|
|
* <p> If you don't hold the {@code android.Manifest.permission#WATCH_APPOPS} permission
|
|
* you can watch changes only for your UID.
|
|
*
|
|
* @param ops The operations to watch.
|
|
* @param callback Where to report changes.
|
|
*
|
|
* @see #stopWatchingActive
|
|
*/
|
|
// TODO: Uncomment below annotation once b/73559440 is fixed
|
|
// @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
|
|
public void startWatchingActive(@NonNull String[] ops,
|
|
@CallbackExecutor @NonNull Executor executor,
|
|
@NonNull OnOpActiveChangedListener callback) {
|
|
Objects.requireNonNull(ops);
|
|
Objects.requireNonNull(executor);
|
|
Objects.requireNonNull(callback);
|
|
IAppOpsActiveCallback cb;
|
|
synchronized (mActiveWatchers) {
|
|
cb = mActiveWatchers.get(callback);
|
|
if (cb != null) {
|
|
return;
|
|
}
|
|
cb = new IAppOpsActiveCallback.Stub() {
|
|
@Override
|
|
public void opActiveChanged(int op, int uid, String packageName,
|
|
String attributionTag, int virtualDeviceId, boolean active,
|
|
@AttributionFlags int attributionFlags, int attributionChainId) {
|
|
executor.execute(() -> {
|
|
if (Flags.deviceAwarePermissionApisEnabled()) {
|
|
if (callback instanceof OnOpActiveChangedInternalListener) {
|
|
((OnOpActiveChangedInternalListener) callback).onOpActiveChanged(op,
|
|
uid, packageName, virtualDeviceId, active);
|
|
}
|
|
if (sAppOpInfos[op].name != null) {
|
|
callback.onOpActiveChanged(sAppOpInfos[op].name, uid, packageName,
|
|
attributionTag, virtualDeviceId, active, attributionFlags,
|
|
attributionChainId);
|
|
}
|
|
} else {
|
|
if (callback instanceof OnOpActiveChangedInternalListener) {
|
|
((OnOpActiveChangedInternalListener) callback).onOpActiveChanged(op,
|
|
uid, packageName, active);
|
|
}
|
|
if (sAppOpInfos[op].name != null) {
|
|
callback.onOpActiveChanged(sAppOpInfos[op].name, uid, packageName,
|
|
attributionTag, active, attributionFlags,
|
|
attributionChainId);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
};
|
|
mActiveWatchers.put(callback, cb);
|
|
}
|
|
final int[] rawOps = new int[ops.length];
|
|
for (int i = 0; i < ops.length; i++) {
|
|
rawOps[i] = strOpToOp(ops[i]);
|
|
}
|
|
try {
|
|
mService.startWatchingActive(rawOps, cb);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stop watching for changes to the active state of an app-op. An app-op may be
|
|
* long running and it has a clear start and stop delimiters. Unregistering a
|
|
* non-registered callback has no effect.
|
|
*
|
|
* @see #startWatchingActive
|
|
*/
|
|
public void stopWatchingActive(@NonNull OnOpActiveChangedListener callback) {
|
|
synchronized (mActiveWatchers) {
|
|
final IAppOpsActiveCallback cb = mActiveWatchers.remove(callback);
|
|
if (cb != null) {
|
|
try {
|
|
mService.stopWatchingActive(cb);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Start watching for started app-ops.
|
|
* An app-op may be long running and it has a clear start delimiter.
|
|
* If an op start is attempted by any package, you will get a callback.
|
|
* To change the watched ops for a registered callback you need to unregister and register it
|
|
* again.
|
|
*
|
|
* <p> If you don't hold the {@code android.Manifest.permission#WATCH_APPOPS} permission
|
|
* you can watch changes only for your UID.
|
|
*
|
|
* @param ops The operations to watch.
|
|
* @param callback Where to report changes.
|
|
*
|
|
* @see #stopWatchingStarted(OnOpStartedListener)
|
|
* @see #startWatchingActive(int[], OnOpActiveChangedListener)
|
|
* @see #startWatchingNoted(int[], OnOpNotedListener)
|
|
* @see #startOp(int, int, String, boolean, String, String)
|
|
* @see #finishOp(int, int, String, String)
|
|
*
|
|
* @hide
|
|
*/
|
|
@RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
|
|
public void startWatchingStarted(@NonNull int[] ops, @NonNull OnOpStartedListener callback) {
|
|
IAppOpsStartedCallback cb;
|
|
synchronized (mStartedWatchers) {
|
|
if (mStartedWatchers.containsKey(callback)) {
|
|
return;
|
|
}
|
|
cb = new IAppOpsStartedCallback.Stub() {
|
|
@Override
|
|
public void opStarted(int op, int uid, String packageName, String attributionTag,
|
|
int virtualDeviceId, int flags, int mode, int startType,
|
|
int attributionFlags, int attributionChainId) {
|
|
callback.onOpStarted(op, uid, packageName, attributionTag, virtualDeviceId,
|
|
flags, mode, startType, attributionFlags, attributionChainId);
|
|
}
|
|
};
|
|
mStartedWatchers.put(callback, cb);
|
|
}
|
|
try {
|
|
mService.startWatchingStarted(ops, cb);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stop watching for started app-ops.
|
|
* An app-op may be long running and it has a clear start delimiter.
|
|
* Henceforth, if an op start is attempted by any package, you will not get a callback.
|
|
* Unregistering a non-registered callback has no effect.
|
|
*
|
|
* @see #startWatchingStarted(int[], OnOpStartedListener)
|
|
* @see #startOp(int, int, String, boolean, String, String)
|
|
*
|
|
* @hide
|
|
*/
|
|
public void stopWatchingStarted(@NonNull OnOpStartedListener callback) {
|
|
synchronized (mStartedWatchers) {
|
|
final IAppOpsStartedCallback cb = mStartedWatchers.remove(callback);
|
|
if (cb != null) {
|
|
try {
|
|
mService.stopWatchingStarted(cb);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Start watching for noted app ops.
|
|
*
|
|
* <p> Similar to {@link #startWatchingNoted(String[], Executor, OnOpNotedListener)}, but
|
|
* without an executor parameter.
|
|
*
|
|
* <p> Note that the listener will be called on the main thread using
|
|
* {@link Context.getMainThread()}. To specify the execution thread, use
|
|
* {@link #startWatchingNoted(String[], Executor, OnOpNotedListener)}.
|
|
*
|
|
* @param ops the ops to watch
|
|
* @param listener listener to notify when an app op is noted
|
|
*
|
|
* @see #startWatchingNoted(String[], Executor, OnOpNotedListener)
|
|
* @see #stopWatchingNoted(OnOpNotedListener)
|
|
* @see #noteOp(String, int, String, String, String)
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
@RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
|
|
public void startWatchingNoted(@NonNull @AppOpString String[] ops,
|
|
@NonNull OnOpNotedListener listener) {
|
|
final int[] intOps = new int[ops.length];
|
|
for (int i = 0; i < ops.length; i++) {
|
|
intOps[i] = strOpToOp(ops[i]);
|
|
}
|
|
startWatchingNoted(intOps, listener);
|
|
}
|
|
|
|
/**
|
|
* Start watching for noted app ops.
|
|
*
|
|
* <p> An app op may be immediate or long-running. Immediate ops are noted while long-running
|
|
* ones are started and stopped.
|
|
*
|
|
* <p> This method allows registering a listener to be notified when an app op is noted. To
|
|
* change the watched ops for a registered callback you need to unregister and register it
|
|
* again.
|
|
*
|
|
* <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission you can
|
|
* watch changes only for your UID.
|
|
*
|
|
* @param ops the ops to watch
|
|
* @param executor the executor on which the listener will be notified
|
|
* @param listener listener to notify when an app op is noted
|
|
*
|
|
* @see #startWatchingNoted(String[], OnOpNotedListener)
|
|
* @see #stopWatchingNoted(OnOpNotedListener)
|
|
* @see #noteOp(String, int, String, String, String)
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
@RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
|
|
public void startWatchingNoted(@NonNull @AppOpString String[] ops,
|
|
@CallbackExecutor @NonNull Executor executor, @NonNull OnOpNotedListener listener) {
|
|
final int[] intOps = new int[ops.length];
|
|
for (int i = 0; i < ops.length; i++) {
|
|
intOps[i] = strOpToOp(ops[i]);
|
|
}
|
|
startWatchingNoted(intOps, executor, listener);
|
|
}
|
|
|
|
/**
|
|
* Start watching for noted app ops.
|
|
*
|
|
* <p> Similar to {@link #startWatchingNoted(int[], Executor, OnOpNotedListener)}, but without
|
|
* an executor parameter.
|
|
*
|
|
* <p> This method is also similar to {@link #startWatchingNoted(String[], OnOpNotedListener)},
|
|
* but allows observing noted ops by their raw op codes instead of string op names.
|
|
*
|
|
* <p> Note that the listener will be called on the main thread using
|
|
* {@link Context.getMainThread()}. To specify the execution thread, use
|
|
* {@link {@link #startWatchingNoted(String[], Executor, OnOpNotedListener)}.
|
|
*
|
|
* @param ops the ops to watch
|
|
* @param listener listener to notify when an app op is noted
|
|
*
|
|
* @see #startWatchingActive(int[], OnOpActiveChangedListener)
|
|
* @see #startWatchingStarted(int[], OnOpStartedListener)
|
|
* @see #startWatchingNoted(String[], OnOpNotedListener)
|
|
* @see #startWatchingNoted(int[], Executor, OnOpNotedListener)
|
|
*
|
|
* @hide
|
|
*/
|
|
@RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
|
|
public void startWatchingNoted(@NonNull int[] ops, @NonNull OnOpNotedListener listener) {
|
|
startWatchingNoted(ops, mContext.getMainExecutor(), listener);
|
|
}
|
|
|
|
/**
|
|
* Start watching for noted app ops.
|
|
*
|
|
* <p> This method is similar to
|
|
* {@link #startWatchingNoted(String[], Executor, OnOpNotedListener)}, but allows observing
|
|
* noted ops by their raw op codes instead of string op names.
|
|
*
|
|
* <p> An app op may be immediate or long-running. Immediate ops are noted while long-running
|
|
* ones are started and stopped.
|
|
*
|
|
* <p> This method allows registering a listener to be notified when an app op is noted. To
|
|
* change the watched ops for a registered callback you need to unregister and register it
|
|
* again.
|
|
*
|
|
* <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission you
|
|
* can watch changes only for your UID.
|
|
*
|
|
* @param ops the ops to watch
|
|
* @param executor the executor on which the listener will be notified
|
|
* @param listener listener to notify when an app op is noted
|
|
*
|
|
* @see #startWatchingActive(int[], OnOpActiveChangedListener)
|
|
* @see #startWatchingStarted(int[], OnOpStartedListener)
|
|
* @see #startWatchingNoted(int[], Executor, OnOpNotedListener)
|
|
* @see #startWatchingNoted(String[], OnOpNotedListener)
|
|
*
|
|
* @hide
|
|
*/
|
|
@RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
|
|
public void startWatchingNoted(@NonNull int[] ops,
|
|
@CallbackExecutor @NonNull Executor executor, @NonNull OnOpNotedListener listener) {
|
|
IAppOpsNotedCallback cb;
|
|
synchronized (mNotedWatchers) {
|
|
cb = mNotedWatchers.get(listener);
|
|
if (cb != null) {
|
|
return;
|
|
}
|
|
cb = new IAppOpsNotedCallback.Stub() {
|
|
@Override
|
|
public void opNoted(int op, int uid, String packageName, String attributionTag,
|
|
int virtualDeviceId, int flags, int mode) {
|
|
final long identity = Binder.clearCallingIdentity();
|
|
try {
|
|
executor.execute(() -> {
|
|
if (sAppOpInfos[op].name != null) {
|
|
if (Flags.deviceAwarePermissionApisEnabled()) {
|
|
listener.onOpNoted(sAppOpInfos[op].name, uid, packageName,
|
|
attributionTag, virtualDeviceId, flags, mode);
|
|
} else {
|
|
listener.onOpNoted(sAppOpInfos[op].name, uid, packageName,
|
|
attributionTag, flags, mode);
|
|
}
|
|
}
|
|
});
|
|
} finally {
|
|
Binder.restoreCallingIdentity(identity);
|
|
}
|
|
}
|
|
};
|
|
mNotedWatchers.put(listener, cb);
|
|
}
|
|
try {
|
|
mService.startWatchingNoted(ops, cb);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stop watching for noted app ops. An app op may be immediate or long running.
|
|
* Unregistering a non-registered callback has no effect.
|
|
*
|
|
* @see #startWatchingNoted(String[], OnOpNotedListener)
|
|
* @see #noteOp(String, int, String, String, String)
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public void stopWatchingNoted(@NonNull OnOpNotedListener callback) {
|
|
synchronized (mNotedWatchers) {
|
|
final IAppOpsNotedCallback cb = mNotedWatchers.remove(callback);
|
|
if (cb != null) {
|
|
try {
|
|
mService.stopWatchingNoted(cb);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private String buildSecurityExceptionMsg(int op, int uid, String packageName) {
|
|
return packageName + " from uid " + uid + " not allowed to perform " +
|
|
sAppOpInfos[op].simpleName;
|
|
}
|
|
|
|
/**
|
|
* {@hide}
|
|
*/
|
|
@UnsupportedAppUsage
|
|
@TestApi
|
|
public static int strOpToOp(@NonNull String op) {
|
|
Integer val = sOpStrToOp.get(op);
|
|
if (val == null) {
|
|
throw new IllegalArgumentException("Unknown operation string: " + op);
|
|
}
|
|
return val;
|
|
}
|
|
|
|
/**
|
|
* Do a quick check for whether an application might be able to perform an operation.
|
|
* This is <em>not</em> a security check; you must use {@link #noteOp(String, int, String,
|
|
* String, String)} or {@link #startOp(String, int, String, String, String)} for your actual
|
|
* security checks, which also ensure that the given uid and package name are consistent. This
|
|
* function can just be used for a quick check to see if an operation has been disabled for the
|
|
* application, as an early reject of some work. This does not modify the time stamp or other
|
|
* data about the operation.
|
|
*
|
|
* <p>Important things this will not do (which you need to ultimate use
|
|
* {@link #noteOp(String, int, String, String, String)} or
|
|
* {@link #startOp(String, int, String, String, String)} to cover):</p>
|
|
* <ul>
|
|
* <li>Verifying the uid and package are consistent, so callers can't spoof
|
|
* their identity.</li>
|
|
* <li>Taking into account the current foreground/background state of the
|
|
* app; apps whose mode varies by this state will always be reported
|
|
* as {@link #MODE_ALLOWED}.</li>
|
|
* </ul>
|
|
*
|
|
* @param op The operation to check. One of the OPSTR_* constants.
|
|
* @param uid The user id of the application attempting to perform the operation.
|
|
* @param packageName The name of the application attempting to perform the operation.
|
|
* @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
|
|
* {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
|
|
* causing the app to crash).
|
|
* @throws SecurityException If the app has been configured to crash on this op.
|
|
*/
|
|
public int unsafeCheckOp(@NonNull String op, int uid, @NonNull String packageName) {
|
|
return checkOp(strOpToOp(op), uid, packageName);
|
|
}
|
|
|
|
/**
|
|
* @deprecated Renamed to {@link #unsafeCheckOp(String, int, String)}.
|
|
*/
|
|
@Deprecated
|
|
public int checkOp(@NonNull String op, int uid, @NonNull String packageName) {
|
|
return checkOp(strOpToOp(op), uid, packageName);
|
|
}
|
|
|
|
/**
|
|
* Like {@link #checkOp} but instead of throwing a {@link SecurityException} it
|
|
* returns {@link #MODE_ERRORED}.
|
|
*/
|
|
public int unsafeCheckOpNoThrow(@NonNull String op, int uid, @NonNull String packageName) {
|
|
return checkOpNoThrow(strOpToOp(op), uid, packageName);
|
|
}
|
|
|
|
/**
|
|
* @deprecated Renamed to {@link #unsafeCheckOpNoThrow(String, int, String)}.
|
|
*/
|
|
@Deprecated
|
|
public int checkOpNoThrow(@NonNull String op, int uid, @NonNull String packageName) {
|
|
return checkOpNoThrow(strOpToOp(op), uid, packageName);
|
|
}
|
|
|
|
/**
|
|
* Like {@link #checkOp} but returns the <em>raw</em> mode associated with the op.
|
|
* Does not throw a security exception, does not translate {@link #MODE_FOREGROUND}.
|
|
*/
|
|
public int unsafeCheckOpRaw(@NonNull String op, int uid, @NonNull String packageName) {
|
|
return unsafeCheckOpRawNoThrow(op, uid, packageName);
|
|
}
|
|
|
|
/**
|
|
* Like {@link #unsafeCheckOpNoThrow(String, int, String)} but returns the <em>raw</em>
|
|
* mode associated with the op. Does not throw a security exception, does not translate
|
|
* {@link #MODE_FOREGROUND}.
|
|
*/
|
|
public int unsafeCheckOpRawNoThrow(@NonNull String op, int uid, @NonNull String packageName) {
|
|
return unsafeCheckOpRawNoThrow(strOpToOp(op), uid, packageName);
|
|
}
|
|
|
|
/**
|
|
* Returns the <em>raw</em> mode associated with the op.
|
|
* Does not throw a security exception, does not translate {@link #MODE_FOREGROUND}.
|
|
* @hide
|
|
*/
|
|
public int unsafeCheckOpRawNoThrow(int op, @NonNull AttributionSource attributionSource) {
|
|
return unsafeCheckOpRawNoThrow(op, attributionSource.getUid(),
|
|
attributionSource.getPackageName(), attributionSource.getDeviceId());
|
|
}
|
|
|
|
/**
|
|
* Returns the <em>raw</em> mode associated with the op.
|
|
* Does not throw a security exception, does not translate {@link #MODE_FOREGROUND}.
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
@SuppressLint("UnflaggedApi") // @TestApi without associated feature.
|
|
public int unsafeCheckOpRawNoThrow(
|
|
@NonNull String op, @NonNull AttributionSource attributionSource) {
|
|
return unsafeCheckOpRawNoThrow(strOpToOp(op), attributionSource);
|
|
}
|
|
|
|
/**
|
|
* Returns the <em>raw</em> mode associated with the op.
|
|
* Does not throw a security exception, does not translate {@link #MODE_FOREGROUND}.
|
|
* @hide
|
|
*/
|
|
public int unsafeCheckOpRawNoThrow(int op, int uid, @NonNull String packageName) {
|
|
return unsafeCheckOpRawNoThrow(op, uid, packageName, Context.DEVICE_ID_DEFAULT);
|
|
}
|
|
|
|
private int unsafeCheckOpRawNoThrow(int op, int uid, @NonNull String packageName,
|
|
int virtualDeviceId) {
|
|
try {
|
|
if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) {
|
|
return mService.checkOperationRaw(op, uid, packageName, null);
|
|
} else {
|
|
return mService.checkOperationRawForDevice(
|
|
op, uid, packageName, null, virtualDeviceId);
|
|
}
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link #noteOp(String, int, String, String, String)} instead
|
|
*/
|
|
@Deprecated
|
|
public int noteOp(@NonNull String op, int uid, @NonNull String packageName) {
|
|
return noteOp(op, uid, packageName, null, null);
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link #noteOp(String, int, String, String, String)} instead
|
|
*
|
|
* @hide
|
|
*/
|
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
|
|
+ "#noteOp(java.lang.String, int, java.lang.String, java.lang.String, "
|
|
+ "java.lang.String)} instead")
|
|
@Deprecated
|
|
public int noteOp(int op) {
|
|
return noteOp(op, Process.myUid(), mContext.getOpPackageName(), null, null);
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link #noteOp(String, int, String, String, String)} instead
|
|
*
|
|
* @hide
|
|
*/
|
|
@Deprecated
|
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
|
|
+ "#noteOp(java.lang.String, int, java.lang.String, java.lang.String, "
|
|
+ "java.lang.String)} instead")
|
|
public int noteOp(int op, int uid, @Nullable String packageName) {
|
|
return noteOp(op, uid, packageName, null, null);
|
|
}
|
|
|
|
/**
|
|
* Make note of an application performing an operation and check if the application is allowed
|
|
* to perform it.
|
|
*
|
|
* <p>If this is a check that is not preceding the protected operation, use
|
|
* {@link #unsafeCheckOp} instead.
|
|
*
|
|
* <p>The identity of the package the app-op is noted for is specified by the
|
|
* {@code uid} and {@code packageName} parameters. If this is noted for a regular app both
|
|
* should be set and the package needs to be part of the uid. In the very rare case that an
|
|
* app-op is noted for an entity that does not have a package name, the package can be
|
|
* {@code null}. As it is possible that a single process contains more than one package the
|
|
* {@code packageName} should be {@link Context#getPackageName() read} from the context of the
|
|
* caller of the API (in the app process) that eventually triggers this check. If this op is
|
|
* not noted for a running process the {@code packageName} cannot be read from the context, but
|
|
* it should be clear which package the note is for.
|
|
*
|
|
* <p>If the {@code uid} and {@code packageName} do not match this return
|
|
* {@link #MODE_IGNORED}.
|
|
*
|
|
* <p>Beside the access check this method also records the access. While the access check is
|
|
* based on {@code uid} and/or {@code packageName} the access recording is done based on the
|
|
* {@code packageName} and {@code attributionTag}. The {@code attributionTag} should be
|
|
* {@link Context#getAttributionTag() read} from the same context the package name is read from.
|
|
* In the case the check is not related to an API call, the {@code attributionTag} should be
|
|
* {@code null}. Please note that e.g. registering a callback for later is still an API call and
|
|
* the code should store the attribution tag along the package name for being used in this
|
|
* method later.
|
|
*
|
|
* <p>The {@code message} parameter only needs to be set when this method is <ul>not</ul>
|
|
* called in a two-way binder call from the client. In this case the message is a free form text
|
|
* that is meant help the app developer determine what part of the app's code triggered the
|
|
* note. This message is passed back to the app in the
|
|
* {@link OnOpNotedCallback#onAsyncNoted(AsyncNotedAppOp)} callback. A good example of a useful
|
|
* message is including the {@link System#identityHashCode(Object)} of the listener that will
|
|
* receive data or the name of the manifest-receiver.
|
|
*
|
|
* @param op The operation to note. One of the OPSTR_* constants.
|
|
* @param uid The uid of the application attempting to perform the operation.
|
|
* @param packageName The name of the application attempting to perform the operation.
|
|
* @param attributionTag The {@link Context#createAttributionContext attribution tag} of the
|
|
* calling context or {@code null} for default attribution
|
|
* @param message A message describing why the op was noted
|
|
*
|
|
* @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
|
|
* {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
|
|
* causing the app to crash).
|
|
*
|
|
* @throws SecurityException If the app has been configured to crash on this op.
|
|
*/
|
|
// For platform callers of this method, please read the package name parameter from
|
|
// Context#getOpPackageName.
|
|
// When noting a callback, the message can be computed using the #toReceiverId method.
|
|
public int noteOp(@NonNull String op, int uid, @Nullable String packageName,
|
|
@Nullable String attributionTag, @Nullable String message) {
|
|
return noteOp(strOpToOp(op), uid, packageName, attributionTag, message);
|
|
}
|
|
|
|
/**
|
|
* @see #noteOp(String, int, String, String, String
|
|
*
|
|
* @hide
|
|
*/
|
|
public int noteOp(int op, int uid, @Nullable String packageName,
|
|
@Nullable String attributionTag, @Nullable String message) {
|
|
final int mode = noteOpNoThrow(op, uid, packageName, attributionTag, message);
|
|
if (mode == MODE_ERRORED) {
|
|
throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
|
|
}
|
|
return mode;
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link #noteOpNoThrow(String, int, String, String, String)} instead
|
|
*/
|
|
@Deprecated
|
|
public int noteOpNoThrow(@NonNull String op, int uid, @NonNull String packageName) {
|
|
return noteOpNoThrow(op, uid, packageName, null, null);
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link #noteOpNoThrow(int, int, String, String, String)} instead
|
|
*
|
|
* @hide
|
|
*/
|
|
@Deprecated
|
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
|
|
+ "#noteOpNoThrow(java.lang.String, int, java.lang.String, java.lang.String, "
|
|
+ "java.lang.String)} instead")
|
|
public int noteOpNoThrow(int op, int uid, String packageName) {
|
|
return noteOpNoThrow(op, uid, packageName, null, null);
|
|
}
|
|
|
|
/**
|
|
* Like {@link #noteOp(String, int, String, String, String)} but instead of throwing a
|
|
* {@link SecurityException} it returns {@link #MODE_ERRORED}.
|
|
*
|
|
* @see #noteOp(String, int, String, String, String)
|
|
*/
|
|
public int noteOpNoThrow(@NonNull String op, int uid, @NonNull String packageName,
|
|
@Nullable String attributionTag, @Nullable String message) {
|
|
return noteOpNoThrow(strOpToOp(op), uid, packageName, attributionTag, message);
|
|
}
|
|
|
|
/**
|
|
* @see #noteOp(String, int, String, String, String)
|
|
*
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
@SuppressLint("UnflaggedApi")
|
|
public int noteOpNoThrow(int op, @NonNull AttributionSource attributionSource,
|
|
@Nullable String message) {
|
|
return noteOpNoThrow(op, attributionSource.getUid(), attributionSource.getPackageName(),
|
|
attributionSource.getAttributionTag(), attributionSource.getDeviceId(), message);
|
|
}
|
|
|
|
/**
|
|
* @see #noteOpNoThrow(String, int, String, String, String)
|
|
*
|
|
* @hide
|
|
*/
|
|
public int noteOpNoThrow(int op, int uid, @Nullable String packageName,
|
|
@Nullable String attributionTag, @Nullable String message) {
|
|
return noteOpNoThrow(op, uid, packageName, attributionTag, Context.DEVICE_ID_DEFAULT,
|
|
message);
|
|
}
|
|
|
|
private int noteOpNoThrow(int op, int uid, @Nullable String packageName,
|
|
@Nullable String attributionTag, int virtualDeviceId, @Nullable String message) {
|
|
try {
|
|
collectNoteOpCallsForValidation(op);
|
|
int collectionMode = getNotedOpCollectionMode(uid, packageName, op);
|
|
boolean shouldCollectMessage = Process.myUid() == Process.SYSTEM_UID;
|
|
if (collectionMode == COLLECT_ASYNC) {
|
|
if (message == null) {
|
|
// Set stack trace as default message
|
|
message = getFormattedStackTrace();
|
|
shouldCollectMessage = true;
|
|
}
|
|
}
|
|
|
|
SyncNotedAppOp syncOp;
|
|
if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) {
|
|
syncOp = mService.noteOperation(op, uid, packageName, attributionTag,
|
|
collectionMode == COLLECT_ASYNC, message, shouldCollectMessage);
|
|
} else {
|
|
syncOp = mService.noteOperationForDevice(op, uid, packageName, attributionTag,
|
|
virtualDeviceId, collectionMode == COLLECT_ASYNC, message,
|
|
shouldCollectMessage);
|
|
}
|
|
if (syncOp.getOpMode() == MODE_ALLOWED) {
|
|
if (collectionMode == COLLECT_SELF) {
|
|
collectNotedOpForSelf(syncOp);
|
|
} else if (collectionMode == COLLECT_SYNC) {
|
|
collectNotedOpSync(syncOp);
|
|
}
|
|
}
|
|
|
|
return syncOp.getOpMode();
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link #noteProxyOp(String, String, int, String, String)} instead
|
|
*/
|
|
@Deprecated
|
|
public int noteProxyOp(@NonNull String op, @NonNull String proxiedPackageName) {
|
|
return noteProxyOp(op, proxiedPackageName, Binder.getCallingUid(), null, null);
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link #noteProxyOp(String, String, int, String, String)} instead
|
|
*
|
|
* @hide
|
|
*/
|
|
@Deprecated
|
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
|
|
+ "#noteProxyOp(java.lang.String, java.lang.String, int, java.lang.String, "
|
|
+ "java.lang.String)} instead")
|
|
public int noteProxyOp(int op, @Nullable String proxiedPackageName) {
|
|
return noteProxyOp(op, proxiedPackageName, Binder.getCallingUid(), null, null);
|
|
}
|
|
|
|
/**
|
|
* @see #noteProxyOp(String, String, int, String, String)
|
|
*
|
|
* @hide
|
|
*/
|
|
public int noteProxyOp(int op, @Nullable String proxiedPackageName, int proxiedUid,
|
|
@Nullable String proxiedAttributionTag, @Nullable String message) {
|
|
return noteProxyOp(op, new AttributionSource(mContext.getAttributionSource(),
|
|
new AttributionSource(proxiedUid, Process.INVALID_PID, proxiedPackageName,
|
|
proxiedAttributionTag, mContext.getAttributionSource().getToken())),
|
|
message, /*skipProxyOperation*/ false);
|
|
}
|
|
|
|
/**
|
|
* Make note of an application performing an operation on behalf of another application when
|
|
* handling an IPC. This function will verify that the calling uid and proxied package name
|
|
* match, and if not, return {@link #MODE_IGNORED}. If this call succeeds, the last execution
|
|
* time of the operation for the proxied app and your app will be updated to the current time.
|
|
*
|
|
* @param op The operation to note. One of the OPSTR_* constants.
|
|
* @param proxiedPackageName The name of the application calling into the proxy application.
|
|
* @param proxiedUid The uid of the proxied application
|
|
* @param proxiedAttributionTag The proxied {@link Context#createAttributionContext
|
|
* attribution tag} or {@code null} for default attribution
|
|
* @param message A message describing the reason the op was noted
|
|
*
|
|
* @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or {@link #MODE_IGNORED}
|
|
* if it is not allowed and should be silently ignored (without causing the app to crash).
|
|
*
|
|
* @throws SecurityException If the proxy or proxied app has been configured to crash on this
|
|
* op.
|
|
*/
|
|
public int noteProxyOp(@NonNull String op, @Nullable String proxiedPackageName, int proxiedUid,
|
|
@Nullable String proxiedAttributionTag, @Nullable String message) {
|
|
return noteProxyOp(strOpToOp(op), proxiedPackageName, proxiedUid, proxiedAttributionTag,
|
|
message);
|
|
}
|
|
|
|
/**
|
|
* Make note of an application performing an operation on behalf of another application(s).
|
|
*
|
|
* @param op The operation to note. One of the OPSTR_* constants.
|
|
* @param attributionSource The permission identity for which to note.
|
|
* @param message A message describing the reason the op was noted
|
|
* @param skipProxyOperation Whether to skip the proxy note.
|
|
*
|
|
* @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or {@link #MODE_IGNORED}
|
|
* if it is not allowed and should be silently ignored (without causing the app to crash).
|
|
*
|
|
* @throws SecurityException If the any proxying operations in the permission identityf
|
|
* chain fails.
|
|
*
|
|
* @hide
|
|
*/
|
|
public int noteProxyOp(@NonNull int op, @NonNull AttributionSource attributionSource,
|
|
@Nullable String message, boolean skipProxyOperation) {
|
|
final int mode = noteProxyOpNoThrow(op, attributionSource, message, skipProxyOperation);
|
|
if (mode == MODE_ERRORED) {
|
|
throw new SecurityException("Proxy package "
|
|
+ attributionSource.getPackageName() + " from uid "
|
|
+ attributionSource.getUid() + " or calling package "
|
|
+ attributionSource.getNextPackageName() + " from uid "
|
|
+ attributionSource.getNextUid() + " not allowed to perform "
|
|
+ sAppOpInfos[op].simpleName);
|
|
}
|
|
return mode;
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link #noteProxyOpNoThrow(String, String, int, String, String)} instead
|
|
*/
|
|
@Deprecated
|
|
public int noteProxyOpNoThrow(@NonNull String op, @NonNull String proxiedPackageName) {
|
|
return noteProxyOpNoThrow(op, proxiedPackageName, Binder.getCallingUid(), null, null);
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link #noteProxyOpNoThrow(String, String, int, String, String)} instead
|
|
*/
|
|
@Deprecated
|
|
public int noteProxyOpNoThrow(@NonNull String op, @Nullable String proxiedPackageName,
|
|
int proxiedUid) {
|
|
return noteProxyOpNoThrow(op, proxiedPackageName, proxiedUid, null, null);
|
|
}
|
|
|
|
/**
|
|
* Like {@link #noteProxyOp(String, String, int, String, String)} but instead
|
|
* of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED}.
|
|
*
|
|
* @see #noteOpNoThrow(String, int, String, String, String)
|
|
*/
|
|
public int noteProxyOpNoThrow(@NonNull String op, @Nullable String proxiedPackageName,
|
|
int proxiedUid, @Nullable String proxiedAttributionTag, @Nullable String message) {
|
|
return noteProxyOpNoThrow(strOpToOp(op), new AttributionSource(
|
|
mContext.getAttributionSource(), new AttributionSource(proxiedUid,
|
|
Process.INVALID_PID, proxiedPackageName, proxiedAttributionTag,
|
|
mContext.getAttributionSource().getToken())), message,
|
|
/*skipProxyOperation*/ false);
|
|
}
|
|
|
|
/**
|
|
* Make note of an application performing an operation on behalf of another application(s).
|
|
*
|
|
* @param op The operation to note. One of the OPSTR_* constants.
|
|
* @param attributionSource The permission identity for which to note.
|
|
* @param message A message describing the reason the op was noted
|
|
* @param skipProxyOperation Whether to note op for the proxy
|
|
*
|
|
* @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or {@link #MODE_IGNORED}
|
|
* if it is not allowed and should be silently ignored (without causing the app to crash).
|
|
*
|
|
* @hide
|
|
*/
|
|
@SuppressWarnings("AndroidFrameworkClientSidePermissionCheck")
|
|
public int noteProxyOpNoThrow(int op, @NonNull AttributionSource attributionSource,
|
|
@Nullable String message, boolean skipProxyOperation) {
|
|
int myUid = Process.myUid();
|
|
|
|
try {
|
|
collectNoteOpCallsForValidation(op);
|
|
int collectionMode = getNotedOpCollectionMode(
|
|
attributionSource.getNextUid(),
|
|
attributionSource.getNextAttributionTag(), op);
|
|
boolean shouldCollectMessage = (myUid == Process.SYSTEM_UID);
|
|
if (collectionMode == COLLECT_ASYNC) {
|
|
if (message == null) {
|
|
// Set stack trace as default message
|
|
message = getFormattedStackTrace();
|
|
shouldCollectMessage = true;
|
|
}
|
|
}
|
|
|
|
SyncNotedAppOp syncOp = mService.noteProxyOperationWithState(op,
|
|
attributionSource.asState(), collectionMode == COLLECT_ASYNC, message,
|
|
shouldCollectMessage, skipProxyOperation);
|
|
|
|
if (syncOp.getOpMode() == MODE_ALLOWED) {
|
|
if (collectionMode == COLLECT_SELF) {
|
|
collectNotedOpForSelf(syncOp);
|
|
} else if (collectionMode == COLLECT_SYNC
|
|
// Only collect app-ops when the proxy is trusted
|
|
&& (mContext.checkPermission(Manifest.permission.UPDATE_APP_OPS_STATS, -1,
|
|
myUid) == PackageManager.PERMISSION_GRANTED ||
|
|
Binder.getCallingUid() == attributionSource.getNextUid())) {
|
|
collectNotedOpSync(syncOp);
|
|
}
|
|
}
|
|
|
|
return syncOp.getOpMode();
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
private static String getComponentPackageNameFromString(String from) {
|
|
ComponentName componentName = from != null ? ComponentName.unflattenFromString(from) : null;
|
|
return componentName != null ? componentName.getPackageName() : "";
|
|
}
|
|
|
|
private static boolean isPackagePreInstalled(Context context, String packageName, int userId) {
|
|
try {
|
|
final PackageManager pm = context.getPackageManager();
|
|
final ApplicationInfo info =
|
|
pm.getApplicationInfoAsUser(packageName, 0, userId);
|
|
return ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
|
|
} catch (PackageManager.NameNotFoundException e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Do a quick check for whether an application might be able to perform an operation.
|
|
* This is <em>not</em> a security check; you must use {@link #noteOp(String, int, String,
|
|
* String, String)} or {@link #startOp(int, int, String, boolean, String, String)} for your
|
|
* actual security checks, which also ensure that the given uid and package name are consistent.
|
|
* This function can just be used for a quick check to see if an operation has been disabled for
|
|
* the application, as an early reject of some work. This does not modify the time stamp or
|
|
* other data about the operation.
|
|
*
|
|
* <p>Important things this will not do (which you need to ultimate use
|
|
* {@link #noteOp(String, int, String, String, String)} or
|
|
* {@link #startOp(int, int, String, boolean, String, String)} to cover):</p>
|
|
* <ul>
|
|
* <li>Verifying the uid and package are consistent, so callers can't spoof
|
|
* their identity.</li>
|
|
* <li>Taking into account the current foreground/background state of the
|
|
* app; apps whose mode varies by this state will always be reported
|
|
* as {@link #MODE_ALLOWED}.</li>
|
|
* </ul>
|
|
*
|
|
* @param op The operation to check. One of the OP_* constants.
|
|
* @param uid The user id of the application attempting to perform the operation.
|
|
* @param packageName The name of the application attempting to perform the operation.
|
|
* @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
|
|
* {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
|
|
* causing the app to crash).
|
|
* @throws SecurityException If the app has been configured to crash on this op.
|
|
* @hide
|
|
*/
|
|
@UnsupportedAppUsage
|
|
public int checkOp(int op, int uid, String packageName) {
|
|
try {
|
|
int mode = mService.checkOperationForDevice(op, uid, packageName,
|
|
Context.DEVICE_ID_DEFAULT);
|
|
if (mode == MODE_ERRORED) {
|
|
throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
|
|
}
|
|
return mode;
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Like {@link #checkOp} but instead of throwing a {@link SecurityException}, it
|
|
* returns {@link #MODE_ERRORED}.
|
|
*
|
|
* @see #checkOp(int, int, String)
|
|
*
|
|
* @hide
|
|
*/
|
|
public int checkOpNoThrow(int op, AttributionSource attributionSource) {
|
|
return checkOpNoThrow(op, attributionSource.getUid(), attributionSource.getPackageName(),
|
|
attributionSource.getDeviceId());
|
|
}
|
|
|
|
/**
|
|
* Like {@link #checkOp} but instead of throwing a {@link SecurityException} it
|
|
* returns {@link #MODE_ERRORED}.
|
|
*
|
|
* @see #checkOp(int, int, String)
|
|
*
|
|
* @hide
|
|
*/
|
|
@UnsupportedAppUsage
|
|
public int checkOpNoThrow(int op, int uid, String packageName) {
|
|
return checkOpNoThrow(op, uid, packageName, Context.DEVICE_ID_DEFAULT);
|
|
}
|
|
|
|
private int checkOpNoThrow(int op, int uid, String packageName, int virtualDeviceId) {
|
|
try {
|
|
int mode;
|
|
if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) {
|
|
mode = mService.checkOperation(op, uid, packageName);
|
|
} else {
|
|
mode = mService.checkOperationForDevice(op, uid, packageName, virtualDeviceId);
|
|
}
|
|
|
|
return mode == AppOpsManager.MODE_FOREGROUND ? AppOpsManager.MODE_ALLOWED : mode;
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link PackageManager#getPackageUid} instead
|
|
*/
|
|
@Deprecated
|
|
public void checkPackage(int uid, @NonNull String packageName) {
|
|
try {
|
|
if (mService.checkPackage(uid, packageName) != MODE_ALLOWED) {
|
|
throw new SecurityException(
|
|
"Package " + packageName + " does not belong to " + uid);
|
|
}
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Like {@link #checkOp} but at a stream-level for audio operations.
|
|
* @hide
|
|
*/
|
|
public int checkAudioOp(int op, int stream, int uid, String packageName) {
|
|
try {
|
|
final int mode = mService.checkAudioOperation(op, stream, uid, packageName);
|
|
if (mode == MODE_ERRORED) {
|
|
throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
|
|
}
|
|
return mode;
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Like {@link #checkAudioOp} but instead of throwing a {@link SecurityException} it
|
|
* returns {@link #MODE_ERRORED}.
|
|
* @hide
|
|
*/
|
|
public int checkAudioOpNoThrow(int op, int stream, int uid, String packageName) {
|
|
try {
|
|
return mService.checkAudioOperation(op, stream, uid, packageName);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use own local {@link android.os.Binder#Binder()}
|
|
*
|
|
* @hide
|
|
*/
|
|
@Deprecated
|
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Create own "
|
|
+ "local {@link android.os.Binder}")
|
|
public static IBinder getToken(IAppOpsService service) {
|
|
return getClientId();
|
|
}
|
|
|
|
/** @hide */
|
|
public static IBinder getClientId() {
|
|
synchronized (AppOpsManager.class) {
|
|
if (sClientId == null) {
|
|
sClientId = new Binder();
|
|
}
|
|
|
|
return sClientId;
|
|
}
|
|
}
|
|
|
|
/** @hide */
|
|
private static IAppOpsService getService() {
|
|
synchronized (sLock) {
|
|
if (sService == null) {
|
|
sService = IAppOpsService.Stub.asInterface(
|
|
ServiceManager.getService(Context.APP_OPS_SERVICE));
|
|
}
|
|
return sService;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @deprecated use {@link #startOp(String, int, String, String, String)} instead
|
|
*/
|
|
@Deprecated
|
|
public int startOp(@NonNull String op, int uid, @NonNull String packageName) {
|
|
return startOp(op, uid, packageName, null, null);
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link #startOp(int, int, String, boolean, String, String)} instead
|
|
*
|
|
* @hide
|
|
*/
|
|
@Deprecated
|
|
public int startOp(int op) {
|
|
return startOp(op, Process.myUid(), mContext.getOpPackageName(), false, null, null);
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link #startOp(int, int, String, boolean, String, String)} instead
|
|
*
|
|
* @hide
|
|
*/
|
|
@Deprecated
|
|
public int startOp(int op, int uid, String packageName) {
|
|
return startOp(op, uid, packageName, false, null, null);
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link #startOp(int, int, String, boolean, String, String)} instead
|
|
*
|
|
* @hide
|
|
*/
|
|
@Deprecated
|
|
public int startOp(int op, int uid, String packageName, boolean startIfModeDefault) {
|
|
return startOp(op, uid, packageName, startIfModeDefault, null, null);
|
|
}
|
|
|
|
/**
|
|
* Report that an application has started executing a long-running operation.
|
|
*
|
|
* <p>For more details how to determine the {@code callingPackageName},
|
|
* {@code callingAttributionTag}, and {@code message}, please check the description in
|
|
* {@link #noteOp(String, int, String, String, String)}
|
|
*
|
|
* @param op The operation to start. One of the OPSTR_* constants.
|
|
* @param uid The user id of the application attempting to perform the operation.
|
|
* @param packageName The name of the application attempting to perform the operation.
|
|
* @param attributionTag The {@link Context#createAttributionContext attribution tag} or
|
|
* {@code null} for default attribution
|
|
* @param message Description why op was started
|
|
*
|
|
* @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
|
|
* {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
|
|
* causing the app to crash).
|
|
*
|
|
* @throws SecurityException If the app has been configured to crash on this op or
|
|
* the package is not in the passed in UID.
|
|
*/
|
|
public int startOp(@NonNull String op, int uid, @Nullable String packageName,
|
|
@Nullable String attributionTag, @Nullable String message) {
|
|
return startOp(strOpToOp(op), uid, packageName, false, attributionTag, message);
|
|
}
|
|
|
|
/**
|
|
* @see #startOp(String, int, String, String, String)
|
|
*
|
|
* @hide
|
|
*/
|
|
public int startOp(int op, int uid, @Nullable String packageName, boolean startIfModeDefault,
|
|
@Nullable String attributionTag, @Nullable String message) {
|
|
final int mode = startOpNoThrow(op, uid, packageName, startIfModeDefault, attributionTag,
|
|
message);
|
|
if (mode == MODE_ERRORED) {
|
|
throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
|
|
}
|
|
return mode;
|
|
}
|
|
|
|
/**
|
|
* @deprecated use {@link #startOpNoThrow(String, int, String, String, String)} instead
|
|
*/
|
|
@Deprecated
|
|
public int startOpNoThrow(@NonNull String op, int uid, @NonNull String packageName) {
|
|
return startOpNoThrow(op, uid, packageName, null, null);
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link #startOpNoThrow(int, int, String, boolean, String, String} instead
|
|
*
|
|
* @hide
|
|
*/
|
|
@Deprecated
|
|
public int startOpNoThrow(int op, int uid, String packageName) {
|
|
return startOpNoThrow(op, uid, packageName, false, null, null);
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link #startOpNoThrow(int, int, String, boolean, String, String} instead
|
|
*
|
|
* @hide
|
|
*/
|
|
@Deprecated
|
|
public int startOpNoThrow(int op, int uid, String packageName, boolean startIfModeDefault) {
|
|
return startOpNoThrow(op, uid, packageName, startIfModeDefault, null, null);
|
|
}
|
|
|
|
/**
|
|
* Like {@link #startOp(String, int, String, String, String)} but instead of throwing a
|
|
* {@link SecurityException} it returns {@link #MODE_ERRORED}.
|
|
*
|
|
* @see #startOp(String, int, String, String, String)
|
|
*/
|
|
public int startOpNoThrow(@NonNull String op, int uid, @NonNull String packageName,
|
|
@Nullable String attributionTag, @Nullable String message) {
|
|
return startOpNoThrow(strOpToOp(op), uid, packageName, false, attributionTag, message);
|
|
}
|
|
|
|
/**
|
|
* @see #startOpNoThrow(String, int, String, String, String)
|
|
*
|
|
* @hide
|
|
*/
|
|
public int startOpNoThrow(int op, int uid, @NonNull String packageName,
|
|
boolean startIfModeDefault, @Nullable String attributionTag, @Nullable String message) {
|
|
return startOpNoThrow(mContext.getAttributionSource().getToken(), op, uid, packageName,
|
|
startIfModeDefault, attributionTag, message);
|
|
}
|
|
|
|
/**
|
|
* @see #startOpNoThrow(String, int, String, String, String)
|
|
*
|
|
* @hide
|
|
*/
|
|
public int startOpNoThrow(@NonNull IBinder token, int op, int uid, @NonNull String packageName,
|
|
boolean startIfModeDefault, @Nullable String attributionTag, @Nullable String message) {
|
|
return startOpNoThrow(token, op, uid, packageName, startIfModeDefault, attributionTag,
|
|
message, ATTRIBUTION_FLAGS_NONE, ATTRIBUTION_CHAIN_ID_NONE);
|
|
}
|
|
|
|
/**
|
|
* @see #startOpNoThrow(String, int, String, String, String)
|
|
*
|
|
* @hide
|
|
*/
|
|
public int startOpNoThrow(@NonNull IBinder token, int op,
|
|
@NonNull AttributionSource attributionSource,
|
|
boolean startIfModeDefault, @Nullable String message,
|
|
@AttributionFlags int attributionFlags, int attributionChainId) {
|
|
return startOpNoThrow(token, op, attributionSource.getUid(),
|
|
attributionSource.getPackageName(), startIfModeDefault,
|
|
attributionSource.getAttributionTag(), attributionSource.getDeviceId(),
|
|
message, attributionFlags, attributionChainId);
|
|
}
|
|
|
|
/**
|
|
* @see #startOpNoThrow(String, int, String, String, String)
|
|
*
|
|
* @hide
|
|
*/
|
|
public int startOpNoThrow(@NonNull IBinder token, int op, int uid, @NonNull String packageName,
|
|
boolean startIfModeDefault, @Nullable String attributionTag, @Nullable String message,
|
|
@AttributionFlags int attributionFlags, int attributionChainId) {
|
|
return startOpNoThrow(token, op, uid, packageName, startIfModeDefault, attributionTag,
|
|
Context.DEVICE_ID_DEFAULT, message, attributionFlags, attributionChainId);
|
|
}
|
|
|
|
private int startOpNoThrow(@NonNull IBinder token, int op, int uid, @NonNull String packageName,
|
|
boolean startIfModeDefault, @Nullable String attributionTag, int virtualDeviceId,
|
|
@Nullable String message, @AttributionFlags int attributionFlags,
|
|
int attributionChainId) {
|
|
try {
|
|
collectNoteOpCallsForValidation(op);
|
|
int collectionMode = getNotedOpCollectionMode(uid, packageName, op);
|
|
boolean shouldCollectMessage = Process.myUid() == Process.SYSTEM_UID;
|
|
if (collectionMode == COLLECT_ASYNC) {
|
|
if (message == null) {
|
|
// Set stack trace as default message
|
|
message = getFormattedStackTrace();
|
|
shouldCollectMessage = true;
|
|
}
|
|
}
|
|
|
|
SyncNotedAppOp syncOp;
|
|
if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) {
|
|
syncOp = mService.startOperation(token, op, uid, packageName,
|
|
attributionTag, startIfModeDefault, collectionMode == COLLECT_ASYNC, message,
|
|
shouldCollectMessage, attributionFlags, attributionChainId);
|
|
} else {
|
|
syncOp = mService.startOperationForDevice(token, op, uid, packageName,
|
|
attributionTag, virtualDeviceId, startIfModeDefault,
|
|
collectionMode == COLLECT_ASYNC, message, shouldCollectMessage,
|
|
attributionFlags, attributionChainId);
|
|
}
|
|
if (syncOp.getOpMode() == MODE_ALLOWED) {
|
|
if (collectionMode == COLLECT_SELF) {
|
|
collectNotedOpForSelf(syncOp);
|
|
} else if (collectionMode == COLLECT_SYNC) {
|
|
collectNotedOpSync(syncOp);
|
|
}
|
|
}
|
|
|
|
return syncOp.getOpMode();
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Report that an application has started executing a long-running operation on behalf of
|
|
* another application when handling an IPC. This function will verify that the calling uid and
|
|
* proxied package name match, and if not, return {@link #MODE_IGNORED}.
|
|
*
|
|
* @param op The op to note
|
|
* @param proxiedUid The uid to note the op for {@code null}
|
|
* @param proxiedPackageName The package name the uid belongs to
|
|
* @param proxiedAttributionTag The proxied {@link Context#createAttributionContext
|
|
* attribution tag} or {@code null} for default attribution
|
|
* @param message A message describing the reason the op was noted
|
|
*
|
|
* @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or {@link #MODE_IGNORED}
|
|
* if it is not allowed and should be silently ignored (without causing the app to crash).
|
|
*
|
|
* @throws SecurityException If the proxy or proxied app has been configured to crash on this
|
|
* op.
|
|
*/
|
|
public int startProxyOp(@NonNull String op, int proxiedUid, @NonNull String proxiedPackageName,
|
|
@Nullable String proxiedAttributionTag, @Nullable String message) {
|
|
return startProxyOp(op, new AttributionSource(mContext.getAttributionSource(),
|
|
new AttributionSource(proxiedUid, Process.INVALID_PID, proxiedPackageName,
|
|
proxiedAttributionTag, mContext.getAttributionSource().getToken())),
|
|
message, /*skipProxyOperation*/ false);
|
|
}
|
|
|
|
/**
|
|
* Report that an application has started executing a long-running operation on behalf of
|
|
* another application for the attribution chain specified by the {@link AttributionSource}}.
|
|
*
|
|
* @param op The op to note
|
|
* @param attributionSource The permission identity for which to check
|
|
* @param message A message describing the reason the op was noted
|
|
* @param skipProxyOperation Whether to skip the proxy start.
|
|
*
|
|
* @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or {@link #MODE_IGNORED}
|
|
* if it is not allowed and should be silently ignored (without causing the app to crash).
|
|
*
|
|
* @throws SecurityException If the any proxying operations in the permission identity
|
|
* chain fails.
|
|
*
|
|
* @hide
|
|
*/
|
|
public int startProxyOp(@NonNull String op, @NonNull AttributionSource attributionSource,
|
|
@Nullable String message, boolean skipProxyOperation) {
|
|
final int mode = startProxyOpNoThrow(AppOpsManager.strOpToOp(op), attributionSource,
|
|
message, skipProxyOperation);
|
|
if (mode == MODE_ERRORED) {
|
|
throw new SecurityException("Proxy package "
|
|
+ attributionSource.getPackageName() + " from uid "
|
|
+ attributionSource.getUid() + " or calling package "
|
|
+ attributionSource.getNextPackageName() + " from uid "
|
|
+ attributionSource.getNextUid() + " not allowed to perform "
|
|
+ op);
|
|
}
|
|
return mode;
|
|
}
|
|
|
|
/**
|
|
* Like {@link #startProxyOp(String, int, String, String, String)} but instead
|
|
* of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED}.
|
|
*
|
|
* @see #startProxyOp(String, int, String, String, String)
|
|
*/
|
|
public int startProxyOpNoThrow(@NonNull String op, int proxiedUid,
|
|
@NonNull String proxiedPackageName, @Nullable String proxiedAttributionTag,
|
|
@Nullable String message) {
|
|
return startProxyOpNoThrow(AppOpsManager.strOpToOp(op), new AttributionSource(
|
|
mContext.getAttributionSource(), new AttributionSource(proxiedUid,
|
|
Process.INVALID_PID, proxiedPackageName, proxiedAttributionTag,
|
|
mContext.getAttributionSource().getToken())), message,
|
|
/*skipProxyOperation*/ false);
|
|
}
|
|
|
|
/**
|
|
* Like {@link #startProxyOp(String, AttributionSource, String)} but instead
|
|
* of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED} and
|
|
* the checks is for the attribution chain specified by the {@link AttributionSource}.
|
|
*
|
|
* @see #startProxyOp(String, AttributionSource, String)
|
|
*
|
|
* @hide
|
|
*/
|
|
public int startProxyOpNoThrow(int op, @NonNull AttributionSource attributionSource,
|
|
@Nullable String message, boolean skipProxyOperation) {
|
|
return startProxyOpNoThrow(attributionSource.getToken(), op, attributionSource, message,
|
|
skipProxyOperation, ATTRIBUTION_FLAGS_NONE, ATTRIBUTION_FLAGS_NONE,
|
|
ATTRIBUTION_CHAIN_ID_NONE);
|
|
}
|
|
|
|
/**
|
|
* Like {@link #startProxyOp(String, AttributionSource, String)} but instead
|
|
* of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED} and
|
|
* the checks is for the attribution chain specified by the {@link AttributionSource}.
|
|
*
|
|
* @see #startProxyOp(String, AttributionSource, String)
|
|
*
|
|
* @hide
|
|
*/
|
|
public int startProxyOpNoThrow(@NonNull IBinder clientId, int op,
|
|
@NonNull AttributionSource attributionSource,
|
|
@Nullable String message, boolean skipProxyOperation, @AttributionFlags
|
|
int proxyAttributionFlags, @AttributionFlags int proxiedAttributionFlags,
|
|
int attributionChainId) {
|
|
try {
|
|
collectNoteOpCallsForValidation(op);
|
|
int collectionMode = getNotedOpCollectionMode(
|
|
attributionSource.getNextUid(),
|
|
attributionSource.getNextPackageName(), op);
|
|
boolean shouldCollectMessage = Process.myUid() == Process.SYSTEM_UID;
|
|
if (collectionMode == COLLECT_ASYNC) {
|
|
if (message == null) {
|
|
// Set stack trace as default message
|
|
message = getFormattedStackTrace();
|
|
shouldCollectMessage = true;
|
|
}
|
|
}
|
|
|
|
SyncNotedAppOp syncOp = mService.startProxyOperationWithState(clientId, op,
|
|
attributionSource.asState(), false, collectionMode == COLLECT_ASYNC, message,
|
|
shouldCollectMessage, skipProxyOperation, proxyAttributionFlags,
|
|
proxiedAttributionFlags, attributionChainId);
|
|
|
|
if (syncOp.getOpMode() == MODE_ALLOWED) {
|
|
if (collectionMode == COLLECT_SELF) {
|
|
collectNotedOpForSelf(syncOp);
|
|
} else if (collectionMode == COLLECT_SYNC
|
|
// Only collect app-ops when the proxy is trusted
|
|
&& (mContext.checkPermission(Manifest.permission.UPDATE_APP_OPS_STATS, -1,
|
|
Process.myUid()) == PackageManager.PERMISSION_GRANTED
|
|
|| Binder.getCallingUid() == attributionSource.getNextUid())) {
|
|
collectNotedOpSync(syncOp);
|
|
}
|
|
}
|
|
|
|
return syncOp.getOpMode();
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link #finishOp(String, int, String, String)} instead
|
|
*
|
|
* @hide
|
|
*/
|
|
@Deprecated
|
|
public void finishOp(int op) {
|
|
finishOp(op, Process.myUid(), mContext.getOpPackageName(), null);
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link #finishOp(String, int, String, String)} instead
|
|
*/
|
|
public void finishOp(@NonNull String op, int uid, @NonNull String packageName) {
|
|
finishOp(strOpToOp(op), uid, packageName, null);
|
|
}
|
|
|
|
/**
|
|
* Report that an application is no longer performing an operation that had previously
|
|
* been started with {@link #startOp(String, int, String, String, String)}. There is no
|
|
* validation of input or result; the parameters supplied here must be the exact same ones
|
|
* previously passed in when starting the operation.
|
|
*/
|
|
public void finishOp(@NonNull String op, int uid, @NonNull String packageName,
|
|
@Nullable String attributionTag) {
|
|
finishOp(strOpToOp(op), uid, packageName, attributionTag);
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link #finishOp(int, int, String, String)} instead
|
|
*
|
|
* @hide
|
|
*/
|
|
public void finishOp(int op, int uid, @NonNull String packageName) {
|
|
finishOp(op, uid, packageName, null);
|
|
}
|
|
|
|
/**
|
|
* @see #finishOp(String, int, String, String)
|
|
*
|
|
* @hide
|
|
*/
|
|
public void finishOp(int op, int uid, @NonNull String packageName,
|
|
@Nullable String attributionTag) {
|
|
finishOp(mContext.getAttributionSource().getToken(), op, uid, packageName, attributionTag);
|
|
}
|
|
|
|
/**
|
|
* @see #finishOp(String, int, String, String)
|
|
*
|
|
* @hide
|
|
*/
|
|
public void finishOp(IBinder token, int op, @NonNull AttributionSource attributionSource) {
|
|
finishOp(token, op, attributionSource.getUid(),
|
|
attributionSource.getPackageName(), attributionSource.getAttributionTag(),
|
|
attributionSource.getDeviceId());
|
|
}
|
|
|
|
/**
|
|
* @see #finishOp(String, int, String, String)
|
|
*
|
|
* @hide
|
|
*/
|
|
public void finishOp(IBinder token, int op, int uid, @NonNull String packageName,
|
|
@Nullable String attributionTag) {
|
|
finishOp(token, op, uid, packageName, attributionTag, Context.DEVICE_ID_DEFAULT);
|
|
}
|
|
|
|
private void finishOp(IBinder token, int op, int uid, @NonNull String packageName,
|
|
@Nullable String attributionTag, int virtualDeviceId ) {
|
|
try {
|
|
if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) {
|
|
mService.finishOperation(token, op, uid, packageName, attributionTag);
|
|
} else {
|
|
mService.finishOperationForDevice(token, op, uid, packageName, attributionTag,
|
|
virtualDeviceId);
|
|
}
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Report that an application is no longer performing an operation that had previously
|
|
* been started with {@link #startProxyOp(String, int, String, String, String)}. There is no
|
|
* validation of input or result; the parameters supplied here must be the exact same ones
|
|
* previously passed in when starting the operation.
|
|
*
|
|
* @param op The operation which was started
|
|
* @param proxiedUid The proxied appp's UID
|
|
* @param proxiedPackageName The proxied appp's package name
|
|
* @param proxiedAttributionTag The proxied appp's attribution tag or
|
|
* {@code null} for default attribution
|
|
*/
|
|
public void finishProxyOp(@NonNull String op, int proxiedUid,
|
|
@NonNull String proxiedPackageName, @Nullable String proxiedAttributionTag) {
|
|
IBinder token = mContext.getAttributionSource().getToken();
|
|
finishProxyOp(token, op, new AttributionSource(mContext.getAttributionSource(),
|
|
new AttributionSource(proxiedUid, Process.INVALID_PID, proxiedPackageName,
|
|
proxiedAttributionTag, token)), /*skipProxyOperation*/ false);
|
|
}
|
|
|
|
/**
|
|
* Report that an application is no longer performing an operation that had previously
|
|
* been started with {@link #startProxyOp(String, AttributionSource, String, boolean)}. There
|
|
* is no validation of input or result; the parameters supplied here must be the exact same
|
|
* ones previously passed in when starting the operation.
|
|
*
|
|
* @param op The operation which was started
|
|
* @param attributionSource The permission identity for which to finish
|
|
* @param skipProxyOperation Whether to skip the proxy finish.
|
|
*
|
|
* @hide
|
|
*/
|
|
public void finishProxyOp(@NonNull IBinder clientId, @NonNull String op,
|
|
@NonNull AttributionSource attributionSource, boolean skipProxyOperation) {
|
|
try {
|
|
mService.finishProxyOperationWithState(
|
|
clientId, strOpToOp(op), attributionSource.asState(), skipProxyOperation);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks whether the given op for a package is active, i.e. did someone call {@link #startOp}
|
|
* without {@link #finishOp} yet.
|
|
* <p>
|
|
* If you don't hold the {@code android.Manifest.permission#WATCH_APPOPS}
|
|
* permission you can query only for your UID.
|
|
*
|
|
* @see #finishOp(String, int, String, String)
|
|
* @see #startOp(String, int, String, String, String)
|
|
*/
|
|
public boolean isOpActive(@NonNull String op, int uid, @NonNull String packageName) {
|
|
return isOperationActive(strOpToOp(op), uid, packageName);
|
|
}
|
|
|
|
/**
|
|
* Get whether you are currently proxying to another package. That applies only
|
|
* for long running operations like {@link #OP_RECORD_AUDIO}.
|
|
*
|
|
* @param op The op.
|
|
* @param proxyAttributionTag Your attribution tag to query for.
|
|
* @param proxiedUid The proxied UID to query for.
|
|
* @param proxiedPackageName The proxied package to query for.
|
|
* @return Whether you are currently proxying to this target.
|
|
*
|
|
* @hide
|
|
*/
|
|
public boolean isProxying(int op, @NonNull String proxyAttributionTag, int proxiedUid,
|
|
@NonNull String proxiedPackageName) {
|
|
try {
|
|
return mService.isProxying(op, mContext.getOpPackageName(),
|
|
mContext.getAttributionTag(), proxiedUid, proxiedPackageName);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clears the op state (last accesses + op modes) for a package but not
|
|
* the historical state.
|
|
*
|
|
* @param packageName The package to reset.
|
|
*
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
@RequiresPermission(Manifest.permission.MANAGE_APPOPS)
|
|
public void resetPackageOpsNoHistory(@NonNull String packageName) {
|
|
try {
|
|
mService.resetPackageOpsNoHistory(packageName);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Start collection of noted appops on this thread.
|
|
*
|
|
* <p>Called at the beginning of a two way binder transaction.
|
|
*
|
|
* @see #finishNotedAppOpsCollection()
|
|
*
|
|
* @hide
|
|
*/
|
|
public static void startNotedAppOpsCollection(int callingUid) {
|
|
sBinderThreadCallingUid.set(callingUid);
|
|
}
|
|
|
|
/**
|
|
* State of a temporarily paused noted app-ops collection.
|
|
*
|
|
* @see #pauseNotedAppOpsCollection()
|
|
*
|
|
* @hide
|
|
*/
|
|
public static class PausedNotedAppOpsCollection {
|
|
final int mUid;
|
|
final @Nullable ArrayMap<String, BitSet> mCollectedNotedAppOps;
|
|
|
|
PausedNotedAppOpsCollection(int uid, @Nullable ArrayMap<String,
|
|
BitSet> collectedNotedAppOps) {
|
|
mUid = uid;
|
|
mCollectedNotedAppOps = collectedNotedAppOps;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Temporarily suspend collection of noted app-ops when binder-thread calls into the other
|
|
* process. During such a call there might be call-backs coming back on the same thread which
|
|
* should not be accounted to the current collection.
|
|
*
|
|
* @return a state needed to resume the collection
|
|
*
|
|
* @hide
|
|
*/
|
|
public static @Nullable PausedNotedAppOpsCollection pauseNotedAppOpsCollection() {
|
|
Integer previousUid = sBinderThreadCallingUid.get();
|
|
if (previousUid != null) {
|
|
ArrayMap<String, BitSet> previousCollectedNotedAppOps =
|
|
sAppOpsNotedInThisBinderTransaction.get();
|
|
|
|
sBinderThreadCallingUid.remove();
|
|
sAppOpsNotedInThisBinderTransaction.remove();
|
|
|
|
return new PausedNotedAppOpsCollection(previousUid, previousCollectedNotedAppOps);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Resume a collection paused via {@link #pauseNotedAppOpsCollection}.
|
|
*
|
|
* @param prevCollection The state of the previous collection
|
|
*
|
|
* @hide
|
|
*/
|
|
public static void resumeNotedAppOpsCollection(
|
|
@Nullable PausedNotedAppOpsCollection prevCollection) {
|
|
if (prevCollection != null) {
|
|
sBinderThreadCallingUid.set(prevCollection.mUid);
|
|
|
|
if (prevCollection.mCollectedNotedAppOps != null) {
|
|
sAppOpsNotedInThisBinderTransaction.set(prevCollection.mCollectedNotedAppOps);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Finish collection of noted appops on this thread.
|
|
*
|
|
* <p>Called at the end of a two way binder transaction.
|
|
*
|
|
* @see #startNotedAppOpsCollection(int)
|
|
*
|
|
* @hide
|
|
*/
|
|
public static void finishNotedAppOpsCollection() {
|
|
sBinderThreadCallingUid.remove();
|
|
sAppOpsNotedInThisBinderTransaction.remove();
|
|
}
|
|
|
|
/**
|
|
* Collect a noted op for the current process.
|
|
*
|
|
* @param op The noted op
|
|
* @param attributionTag The attribution tag the op is noted for
|
|
*/
|
|
private void collectNotedOpForSelf(SyncNotedAppOp syncOp) {
|
|
synchronized (sLock) {
|
|
if (sOnOpNotedCallback != null) {
|
|
sOnOpNotedCallback.onSelfNoted(syncOp);
|
|
}
|
|
}
|
|
sMessageCollector.onSelfNoted(syncOp);
|
|
}
|
|
|
|
/**
|
|
* Collect a noted op when inside of a two-way binder call.
|
|
*
|
|
* <p> Delivered to caller via {@link #prefixParcelWithAppOpsIfNeeded}
|
|
*
|
|
* @param syncOp the op and attribution tag to note for
|
|
*
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
public static void collectNotedOpSync(@NonNull SyncNotedAppOp syncOp) {
|
|
// If this is inside of a two-way binder call:
|
|
// We are inside of a two-way binder call. Delivered to caller via
|
|
// {@link #prefixParcelWithAppOpsIfNeeded}
|
|
int op = sOpStrToOp.get(syncOp.getOp());
|
|
ArrayMap<String, BitSet> appOpsNoted = sAppOpsNotedInThisBinderTransaction.get();
|
|
if (appOpsNoted == null) {
|
|
appOpsNoted = new ArrayMap<>(1);
|
|
sAppOpsNotedInThisBinderTransaction.set(appOpsNoted);
|
|
}
|
|
|
|
BitSet appOpsNotedForAttribution = appOpsNoted.get(syncOp.getAttributionTag());
|
|
if (appOpsNotedForAttribution == null) {
|
|
appOpsNotedForAttribution = new BitSet(_NUM_OP);
|
|
appOpsNoted.put(syncOp.getAttributionTag(), appOpsNotedForAttribution);
|
|
}
|
|
|
|
appOpsNotedForAttribution.set(op);
|
|
}
|
|
|
|
/**
|
|
* Get recent op usage data for CAMERA, MICROPHONE and LOCATION from all connected devices
|
|
* to power privacy indicator.
|
|
*
|
|
* @param includeMicrophoneUsage whether to retrieve microphone usage
|
|
* @return A list of permission groups currently or recently used by all apps by all users in
|
|
* the current profile group.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
@NonNull
|
|
@FlaggedApi(android.permission.flags.Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
|
|
@RequiresPermission(Manifest.permission.GET_APP_OPS_STATS)
|
|
public List<PermissionGroupUsage> getPermissionGroupUsageForPrivacyIndicator(
|
|
boolean includeMicrophoneUsage) {
|
|
// Lazily initialize the usage helper
|
|
if (mUsageHelper == null) {
|
|
mUsageHelper = new PermissionUsageHelper(mContext);
|
|
}
|
|
|
|
return mUsageHelper.getOpUsageDataForAllDevices(includeMicrophoneUsage);
|
|
}
|
|
|
|
/** @hide */
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@IntDef(value = {
|
|
DONT_COLLECT,
|
|
COLLECT_SELF,
|
|
COLLECT_SYNC,
|
|
COLLECT_ASYNC
|
|
})
|
|
private @interface NotedOpCollectionMode {}
|
|
private static final int DONT_COLLECT = 0;
|
|
private static final int COLLECT_SELF = 1;
|
|
private static final int COLLECT_SYNC = 2;
|
|
private static final int COLLECT_ASYNC = 3;
|
|
|
|
/**
|
|
* Mark an app-op as noted.
|
|
*/
|
|
private @NotedOpCollectionMode int getNotedOpCollectionMode(int uid,
|
|
@Nullable String packageName, int op) {
|
|
if (packageName == null) {
|
|
packageName = "android";
|
|
}
|
|
|
|
// check if the appops needs to be collected and cache result
|
|
if (sAppOpsToNote[op] == SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED) {
|
|
boolean shouldCollectNotes;
|
|
try {
|
|
shouldCollectNotes = mService.shouldCollectNotes(op);
|
|
} catch (RemoteException e) {
|
|
return DONT_COLLECT;
|
|
}
|
|
|
|
if (shouldCollectNotes) {
|
|
sAppOpsToNote[op] = SHOULD_COLLECT_NOTE_OP;
|
|
} else {
|
|
sAppOpsToNote[op] = SHOULD_NOT_COLLECT_NOTE_OP;
|
|
}
|
|
}
|
|
|
|
if (sAppOpsToNote[op] != SHOULD_COLLECT_NOTE_OP) {
|
|
return DONT_COLLECT;
|
|
}
|
|
|
|
synchronized (sLock) {
|
|
if (uid == Process.myUid()
|
|
&& packageName.equals(ActivityThread.currentOpPackageName())) {
|
|
return COLLECT_SELF;
|
|
}
|
|
}
|
|
|
|
Integer binderUid = sBinderThreadCallingUid.get();
|
|
|
|
if (binderUid != null && binderUid == uid) {
|
|
return COLLECT_SYNC;
|
|
} else {
|
|
return COLLECT_ASYNC;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Append app-ops noted in the current two-way binder transaction to parcel.
|
|
*
|
|
* <p>This is called on the callee side of a two way binder transaction just before the
|
|
* transaction returns.
|
|
*
|
|
* @param p the parcel to append the noted app-ops to
|
|
*
|
|
* @hide
|
|
*/
|
|
// TODO (b/186872903) Refactor how sync noted ops are propagated.
|
|
public static void prefixParcelWithAppOpsIfNeeded(@NonNull Parcel p) {
|
|
ArrayMap<String, BitSet> notedAppOps = sAppOpsNotedInThisBinderTransaction.get();
|
|
if (notedAppOps == null) {
|
|
return;
|
|
}
|
|
|
|
p.writeInt(Parcel.EX_HAS_NOTED_APPOPS_REPLY_HEADER);
|
|
|
|
int numAttributionWithNotesAppOps = notedAppOps.size();
|
|
p.writeInt(numAttributionWithNotesAppOps);
|
|
|
|
for (int i = 0; i < numAttributionWithNotesAppOps; i++) {
|
|
p.writeString(notedAppOps.keyAt(i));
|
|
// Bitmask's toLongArray will truncate the array, if upper bits arent used
|
|
long[] notedOpsMask = notedAppOps.valueAt(i).toLongArray();
|
|
for (int j = 0; j < BITMASK_LEN; j++) {
|
|
if (j < notedOpsMask.length) {
|
|
p.writeLong(notedOpsMask[j]);
|
|
} else {
|
|
p.writeLong(0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Read app-ops noted during a two-way binder transaction from parcel.
|
|
*
|
|
* <p>This is called on the calling side of a two way binder transaction just after the
|
|
* transaction returns.
|
|
*
|
|
* @param p The parcel to read from
|
|
*
|
|
* @hide
|
|
*/
|
|
public static void readAndLogNotedAppops(@NonNull Parcel p) {
|
|
int numAttributionsWithNotedAppOps = p.readInt();
|
|
|
|
for (int i = 0; i < numAttributionsWithNotedAppOps; i++) {
|
|
String attributionTag = p.readString();
|
|
long[] rawNotedAppOps = new long[BITMASK_LEN];
|
|
for (int j = 0; j < rawNotedAppOps.length; j++) {
|
|
rawNotedAppOps[j] = p.readLong();
|
|
}
|
|
BitSet notedAppOps = BitSet.valueOf(rawNotedAppOps);
|
|
|
|
if (!notedAppOps.isEmpty()) {
|
|
|
|
synchronized (sLock) {
|
|
for (int code = notedAppOps.nextSetBit(0); code != -1;
|
|
code = notedAppOps.nextSetBit(code + 1)) {
|
|
if (sOnOpNotedCallback != null) {
|
|
sOnOpNotedCallback.onNoted(new SyncNotedAppOp(code, attributionTag));
|
|
} else {
|
|
String message = getFormattedStackTrace();
|
|
sUnforwardedOps.add(
|
|
new AsyncNotedAppOp(code, Process.myUid(), attributionTag,
|
|
message, System.currentTimeMillis()));
|
|
if (sUnforwardedOps.size() > MAX_UNFORWARDED_OPS) {
|
|
sUnforwardedOps.remove(0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (int code = notedAppOps.nextSetBit(0); code != -1;
|
|
code = notedAppOps.nextSetBit(code + 1)) {
|
|
sMessageCollector.onNoted(new SyncNotedAppOp(code, attributionTag));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set a new {@link OnOpNotedCallback}.
|
|
*
|
|
* <p>There can only ever be one collector per process. If there currently is another callback
|
|
* set, this will fail.
|
|
*
|
|
* @param asyncExecutor executor to execute {@link OnOpNotedCallback#onAsyncNoted} on, {@code
|
|
* null} to unset
|
|
* @param callback listener to set, {@code null} to unset
|
|
*
|
|
* @throws IllegalStateException If another callback is already registered
|
|
*/
|
|
public void setOnOpNotedCallback(@Nullable @CallbackExecutor Executor asyncExecutor,
|
|
@Nullable OnOpNotedCallback callback) {
|
|
Preconditions.checkState((callback == null) == (asyncExecutor == null));
|
|
|
|
synchronized (sLock) {
|
|
if (callback == null) {
|
|
Preconditions.checkState(sOnOpNotedCallback != null,
|
|
"No callback is currently registered");
|
|
|
|
try {
|
|
mService.stopWatchingAsyncNoted(mContext.getPackageName(),
|
|
sOnOpNotedCallback.mAsyncCb);
|
|
} catch (RemoteException e) {
|
|
e.rethrowFromSystemServer();
|
|
}
|
|
|
|
sOnOpNotedCallback = null;
|
|
} else {
|
|
Preconditions.checkState(sOnOpNotedCallback == null,
|
|
"Another callback is already registered");
|
|
|
|
callback.mAsyncExecutor = asyncExecutor;
|
|
sOnOpNotedCallback = callback;
|
|
|
|
List<AsyncNotedAppOp> missedAsyncOps = null;
|
|
try {
|
|
mService.startWatchingAsyncNoted(mContext.getPackageName(),
|
|
sOnOpNotedCallback.mAsyncCb);
|
|
missedAsyncOps = mService.extractAsyncOps(mContext.getPackageName());
|
|
} catch (RemoteException e) {
|
|
e.rethrowFromSystemServer();
|
|
}
|
|
|
|
// Copy pointer so callback can be dispatched out of lock
|
|
OnOpNotedCallback onOpNotedCallback = sOnOpNotedCallback;
|
|
if (onOpNotedCallback != null && missedAsyncOps != null) {
|
|
int numMissedAsyncOps = missedAsyncOps.size();
|
|
for (int i = 0; i < numMissedAsyncOps; i++) {
|
|
final AsyncNotedAppOp asyncNotedAppOp = missedAsyncOps.get(i);
|
|
onOpNotedCallback.getAsyncNotedExecutor().execute(
|
|
() -> onOpNotedCallback.onAsyncNoted(asyncNotedAppOp));
|
|
}
|
|
}
|
|
synchronized (this) {
|
|
int numMissedSyncOps = sUnforwardedOps.size();
|
|
if (onOpNotedCallback != null) {
|
|
for (int i = 0; i < numMissedSyncOps; i++) {
|
|
final AsyncNotedAppOp syncNotedAppOp = sUnforwardedOps.get(i);
|
|
onOpNotedCallback.getAsyncNotedExecutor().execute(
|
|
() -> onOpNotedCallback.onAsyncNoted(syncNotedAppOp));
|
|
}
|
|
}
|
|
sUnforwardedOps.clear();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO moltmann: Remove
|
|
/**
|
|
* Will be removed before R ships, leave it just to not break apps immediately.
|
|
*
|
|
* @removed
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
@Deprecated
|
|
public void setNotedAppOpsCollector(@Nullable AppOpsCollector collector) {
|
|
synchronized (sLock) {
|
|
if (collector != null) {
|
|
if (isListeningForOpNoted()) {
|
|
setOnOpNotedCallback(null, null);
|
|
}
|
|
setOnOpNotedCallback(new HandlerExecutor(Handler.getMain()), collector);
|
|
} else if (sOnOpNotedCallback != null) {
|
|
setOnOpNotedCallback(null, null);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return {@code true} iff the process currently is currently collecting noted appops.
|
|
*
|
|
* @see #setOnOpNotedCallback
|
|
*
|
|
* @hide
|
|
*/
|
|
public static boolean isListeningForOpNoted() {
|
|
return sOnOpNotedCallback != null || isCollectingStackTraces();
|
|
}
|
|
|
|
/**
|
|
* @return {@code true} iff the process is currently sampled for stacktrace collection.
|
|
*
|
|
* @see #setOnOpNotedCallback
|
|
*
|
|
* @hide
|
|
*/
|
|
private static boolean isCollectingStackTraces() {
|
|
if (sConfig.getSampledOpCode() == OP_NONE && sConfig.getAcceptableLeftDistance() == 0 &&
|
|
sConfig.getExpirationTimeSinceBootMillis() >= SystemClock.elapsedRealtime()) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Callback an app can {@link #setOnOpNotedCallback set} to monitor the app-ops the
|
|
* system has tracked for it. I.e. each time any app calls {@link #noteOp} or {@link #startOp}
|
|
* one of a method of this object is called.
|
|
*
|
|
* <p><b>There will be a call for all app-ops related to runtime permissions, but not
|
|
* necessarily for all other app-ops.
|
|
*
|
|
* <pre>
|
|
* setOnOpNotedCallback(getMainExecutor(), new OnOpNotedCallback() {
|
|
* ArraySet<Pair<String, String>> opsNotedForThisProcess = new ArraySet<>();
|
|
*
|
|
* private synchronized void addAccess(String op, String accessLocation) {
|
|
* // Ops are often noted when runtime permission protected APIs were called.
|
|
* // In this case permissionToOp() allows to resolve the permission<->op
|
|
* opsNotedForThisProcess.add(new Pair(accessType, accessLocation));
|
|
* }
|
|
*
|
|
* public void onNoted(SyncNotedAppOp op) {
|
|
* // Accesses is currently happening, hence stack trace describes location of access
|
|
* addAccess(op.getOp(), Arrays.toString(Thread.currentThread().getStackTrace()));
|
|
* }
|
|
*
|
|
* public void onSelfNoted(SyncNotedAppOp op) {
|
|
* onNoted(op);
|
|
* }
|
|
*
|
|
* public void onAsyncNoted(AsyncNotedAppOp asyncOp) {
|
|
* // Stack trace is not useful for async ops as accessed happened on different thread
|
|
* addAccess(asyncOp.getOp(), asyncOp.getMessage());
|
|
* }
|
|
* });
|
|
* </pre>
|
|
*
|
|
* @see #setOnOpNotedCallback
|
|
*/
|
|
public abstract static class OnOpNotedCallback {
|
|
private @NonNull Executor mAsyncExecutor;
|
|
|
|
/** Callback registered with the system. This will receive the async notes ops */
|
|
private final IAppOpsAsyncNotedCallback mAsyncCb = new IAppOpsAsyncNotedCallback.Stub() {
|
|
@Override
|
|
public void opNoted(AsyncNotedAppOp op) {
|
|
Objects.requireNonNull(op);
|
|
|
|
final long token = Binder.clearCallingIdentity();
|
|
try {
|
|
getAsyncNotedExecutor().execute(() -> onAsyncNoted(op));
|
|
} finally {
|
|
Binder.restoreCallingIdentity(token);
|
|
}
|
|
}
|
|
};
|
|
|
|
// TODO moltmann: Remove
|
|
/**
|
|
* Will be removed before R ships.
|
|
*
|
|
* @return The executor for the system to use when calling {@link #onAsyncNoted}.
|
|
*
|
|
* @hide
|
|
*/
|
|
protected @NonNull Executor getAsyncNotedExecutor() {
|
|
return mAsyncExecutor;
|
|
}
|
|
|
|
/**
|
|
* Called when an app-op was {@link #noteOp noted} for this package inside of a synchronous
|
|
* API call, i.e. a API call that returned data or waited until the action was performed.
|
|
*
|
|
* <p>Called on the calling thread before the API returns. This allows the app to e.g.
|
|
* collect stack traces to figure out where the access came from.
|
|
*
|
|
* @param op op noted
|
|
*/
|
|
public abstract void onNoted(@NonNull SyncNotedAppOp op);
|
|
|
|
/**
|
|
* Called when this app noted an app-op for its own package,
|
|
*
|
|
* <p>This is very similar to {@link #onNoted} only that the tracking was not caused by the
|
|
* API provider in a separate process, but by one in the app's own process.
|
|
*
|
|
* @param op op noted
|
|
*/
|
|
public abstract void onSelfNoted(@NonNull SyncNotedAppOp op);
|
|
|
|
/**
|
|
* Called when an app-op was noted for this package which cannot be delivered via the other
|
|
* two mechanisms.
|
|
*
|
|
* <p>Called as soon as possible after the app-op was noted, but the delivery delay is not
|
|
* guaranteed. Due to how async calls work in Android this might even be delivered slightly
|
|
* before the private data is delivered to the app.
|
|
*
|
|
* <p>If the app is not running or no {@link OnOpNotedCallback} is registered a small amount
|
|
* of noted app-ops are buffered and then delivered as soon as a listener is registered.
|
|
*
|
|
* @param asyncOp op noted
|
|
*/
|
|
public abstract void onAsyncNoted(@NonNull AsyncNotedAppOp asyncOp);
|
|
}
|
|
|
|
// TODO moltmann: Remove
|
|
/**
|
|
* Will be removed before R ships, leave it just to not break apps immediately.
|
|
*
|
|
* @removed
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
@Deprecated
|
|
public abstract static class AppOpsCollector extends OnOpNotedCallback {
|
|
public @NonNull Executor getAsyncNotedExecutor() {
|
|
return new HandlerExecutor(Handler.getMain());
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Generate a stack trace used for noted app-ops logging.
|
|
*
|
|
* <p>This strips away the first few and last few stack trace elements as they are not
|
|
* interesting to apps.
|
|
*/
|
|
private static String getFormattedStackTrace() {
|
|
StackTraceElement[] trace = new Exception().getStackTrace();
|
|
|
|
int firstInteresting = 0;
|
|
for (int i = 0; i < trace.length; i++) {
|
|
if (trace[i].getClassName().startsWith(AppOpsManager.class.getName())
|
|
|| trace[i].getClassName().startsWith(Parcel.class.getName())
|
|
|| trace[i].getClassName().contains("$Stub$Proxy")
|
|
|| trace[i].getClassName().startsWith(DatabaseUtils.class.getName())
|
|
|| trace[i].getClassName().startsWith("android.content.ContentProviderProxy")
|
|
|| trace[i].getClassName().startsWith(ContentResolver.class.getName())) {
|
|
firstInteresting = i;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
int lastInteresting = trace.length - 1;
|
|
for (int i = trace.length - 1; i >= 0; i--) {
|
|
if (trace[i].getClassName().startsWith(HandlerThread.class.getName())
|
|
|| trace[i].getClassName().startsWith(Handler.class.getName())
|
|
|| trace[i].getClassName().startsWith(Looper.class.getName())
|
|
|| trace[i].getClassName().startsWith(Binder.class.getName())
|
|
|| trace[i].getClassName().startsWith(RuntimeInit.class.getName())
|
|
|| trace[i].getClassName().startsWith(ZygoteInit.class.getName())
|
|
|| trace[i].getClassName().startsWith(ActivityThread.class.getName())
|
|
|| trace[i].getClassName().startsWith(Method.class.getName())
|
|
|| trace[i].getClassName().startsWith("com.android.server.SystemServer")) {
|
|
lastInteresting = i;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
for (int i = firstInteresting; i <= lastInteresting; i++) {
|
|
if (sFullLog == null) {
|
|
try {
|
|
sFullLog = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
|
|
FULL_LOG, false);
|
|
} catch (Exception e) {
|
|
// This should not happen, but it may, in rare cases
|
|
sFullLog = false;
|
|
}
|
|
}
|
|
|
|
if (i != firstInteresting) {
|
|
sb.append('\n');
|
|
}
|
|
final String traceString = trace[i].toString();
|
|
if (!sFullLog && sb.length() + traceString.length() > 600) {
|
|
break;
|
|
}
|
|
sb.append(traceString);
|
|
}
|
|
|
|
return sb.toString();
|
|
}
|
|
|
|
/**
|
|
* Checks whether the given op for a UID and package is active.
|
|
*
|
|
* <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission
|
|
* you can query only for your UID.
|
|
*
|
|
* @see #startWatchingActive(int[], OnOpActiveChangedListener)
|
|
* @see #stopWatchingMode(OnOpChangedListener)
|
|
* @see #finishOp(int, int, String, String)
|
|
* @see #startOp(int, int, String, boolean, String, String)
|
|
*
|
|
* @hide */
|
|
@TestApi
|
|
// TODO: Uncomment below annotation once b/73559440 is fixed
|
|
// @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
|
|
public boolean isOperationActive(int code, int uid, String packageName) {
|
|
try {
|
|
return mService.isOperationActive(code, uid, packageName);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Configures the app ops persistence for testing.
|
|
*
|
|
* @param mode The mode in which the historical registry operates.
|
|
* @param baseSnapshotInterval The base interval on which we would be persisting a snapshot of
|
|
* the historical data. The history is recursive where every subsequent step encompasses
|
|
* {@code compressionStep} longer interval with {@code compressionStep} distance between
|
|
* snapshots.
|
|
* @param compressionStep The compression step in every iteration.
|
|
*
|
|
* @see #HISTORICAL_MODE_DISABLED
|
|
* @see #HISTORICAL_MODE_ENABLED_ACTIVE
|
|
* @see #HISTORICAL_MODE_ENABLED_PASSIVE
|
|
*
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
@RequiresPermission(Manifest.permission.MANAGE_APPOPS)
|
|
public void setHistoryParameters(@HistoricalMode int mode, long baseSnapshotInterval,
|
|
int compressionStep) {
|
|
try {
|
|
mService.setHistoryParameters(mode, baseSnapshotInterval, compressionStep);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Offsets the history by the given duration.
|
|
*
|
|
* @param offsetMillis The offset duration.
|
|
*
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
@RequiresPermission(Manifest.permission.MANAGE_APPOPS)
|
|
public void offsetHistory(long offsetMillis) {
|
|
try {
|
|
mService.offsetHistory(offsetMillis);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds ops to the history directly. This could be useful for testing especially
|
|
* when the historical registry operates in {@link #HISTORICAL_MODE_ENABLED_PASSIVE}
|
|
* mode.
|
|
*
|
|
* @param ops The ops to add to the history.
|
|
*
|
|
* @see #setHistoryParameters(int, long, int)
|
|
* @see #HISTORICAL_MODE_ENABLED_PASSIVE
|
|
*
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
@RequiresPermission(Manifest.permission.MANAGE_APPOPS)
|
|
public void addHistoricalOps(@NonNull HistoricalOps ops) {
|
|
try {
|
|
mService.addHistoricalOps(ops);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Resets the app ops persistence for testing.
|
|
*
|
|
* @see #setHistoryParameters(int, long, int)
|
|
*
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
@RequiresPermission(Manifest.permission.MANAGE_APPOPS)
|
|
public void resetHistoryParameters() {
|
|
try {
|
|
mService.resetHistoryParameters();
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clears all app ops history.
|
|
*
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
@RequiresPermission(Manifest.permission.MANAGE_APPOPS)
|
|
public void clearHistory() {
|
|
try {
|
|
mService.clearHistory();
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reboots the ops history.
|
|
*
|
|
* @param offlineDurationMillis The duration to wait between
|
|
* tearing down and initializing the history. Must be greater
|
|
* than or equal to zero.
|
|
*
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
@RequiresPermission(Manifest.permission.MANAGE_APPOPS)
|
|
public void rebootHistory(long offlineDurationMillis) {
|
|
try {
|
|
mService.rebootHistory(offlineDurationMillis);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Pulls current AppOps access report and picks package and op to watch for next access report
|
|
* Returns null if no reports were collected since last call. There is no guarantee of report
|
|
* collection, hence this method should be called periodically even if no report was collected
|
|
* to pick different package and op to watch.
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
@RequiresPermission(Manifest.permission.GET_APP_OPS_STATS)
|
|
public @Nullable RuntimeAppOpAccessMessage collectRuntimeAppOpAccessMessage() {
|
|
try {
|
|
return mService.collectRuntimeAppOpAccessMessage();
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns all supported operation names.
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public static String[] getOpStrs() {
|
|
String[] opStrs = new String[sAppOpInfos.length];
|
|
for(int i = 0; i < sAppOpInfos.length; i++) {
|
|
opStrs[i] = sAppOpInfos[i].name;
|
|
}
|
|
return opStrs;
|
|
}
|
|
|
|
/**
|
|
* @return number of App ops
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
public static int getNumOps() {
|
|
return _NUM_OP;
|
|
}
|
|
|
|
/**
|
|
* Gets the last of the event.
|
|
*
|
|
* @param events The events
|
|
* @param flags The UID flags
|
|
* @param beginUidState The maximum UID state (inclusive)
|
|
* @param endUidState The minimum UID state (inclusive)
|
|
*
|
|
* @return The last event of {@code null}
|
|
*/
|
|
private static @Nullable NoteOpEvent getLastEvent(
|
|
@Nullable LongSparseArray<NoteOpEvent> events, @UidState int beginUidState,
|
|
@UidState int endUidState, @OpFlags int flags) {
|
|
if (events == null) {
|
|
return null;
|
|
}
|
|
|
|
NoteOpEvent lastEvent = null;
|
|
while (flags != 0) {
|
|
final int flag = 1 << Integer.numberOfTrailingZeros(flags);
|
|
flags &= ~flag;
|
|
for (int uidState : UID_STATES) {
|
|
if (uidState < beginUidState || uidState > endUidState) {
|
|
continue;
|
|
}
|
|
final long key = makeKey(uidState, flag);
|
|
|
|
NoteOpEvent event = events.get(key);
|
|
if (lastEvent == null
|
|
|| event != null && event.getNoteTime() > lastEvent.getNoteTime()) {
|
|
lastEvent = event;
|
|
}
|
|
}
|
|
}
|
|
|
|
return lastEvent;
|
|
}
|
|
|
|
private static boolean equalsLongSparseLongArray(@Nullable LongSparseLongArray a,
|
|
@Nullable LongSparseLongArray b) {
|
|
if (a == b) {
|
|
return true;
|
|
}
|
|
|
|
if (a == null || b == null) {
|
|
return false;
|
|
}
|
|
|
|
if (a.size() != b.size()) {
|
|
return false;
|
|
}
|
|
|
|
int numEntries = a.size();
|
|
for (int i = 0; i < numEntries; i++) {
|
|
if (a.keyAt(i) != b.keyAt(i) || a.valueAt(i) != b.valueAt(i)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private static void writeLongSparseLongArrayToParcel(
|
|
@Nullable LongSparseLongArray array, @NonNull Parcel parcel) {
|
|
if (array != null) {
|
|
final int size = array.size();
|
|
parcel.writeInt(size);
|
|
for (int i = 0; i < size; i++) {
|
|
parcel.writeLong(array.keyAt(i));
|
|
parcel.writeLong(array.valueAt(i));
|
|
}
|
|
} else {
|
|
parcel.writeInt(-1);
|
|
}
|
|
}
|
|
|
|
private static @Nullable LongSparseLongArray readLongSparseLongArrayFromParcel(
|
|
@NonNull Parcel parcel) {
|
|
final int size = parcel.readInt();
|
|
if (size < 0) {
|
|
return null;
|
|
}
|
|
final LongSparseLongArray array = new LongSparseLongArray(size);
|
|
for (int i = 0; i < size; i++) {
|
|
array.append(parcel.readLong(), parcel.readLong());
|
|
}
|
|
return array;
|
|
}
|
|
|
|
private static void writeDiscreteAccessArrayToParcel(
|
|
@Nullable List<AttributedOpEntry> array, @NonNull Parcel parcel, int flags) {
|
|
ParceledListSlice<AttributedOpEntry> listSlice =
|
|
array == null ? null : new ParceledListSlice<>(array);
|
|
parcel.writeParcelable(listSlice, flags);
|
|
}
|
|
|
|
private static @Nullable List<AttributedOpEntry> readDiscreteAccessArrayFromParcel(
|
|
@NonNull Parcel parcel) {
|
|
final ParceledListSlice<AttributedOpEntry> listSlice = parcel.readParcelable(null, android.content.pm.ParceledListSlice.class);
|
|
return listSlice == null ? null : listSlice.getList();
|
|
}
|
|
|
|
/**
|
|
* Collects the keys from an array to the result creating the result if needed.
|
|
*
|
|
* @param array The array whose keys to collect.
|
|
* @param result The optional result store collected keys.
|
|
* @return The result collected keys array.
|
|
*/
|
|
private static LongSparseArray<Object> collectKeys(@Nullable LongSparseLongArray array,
|
|
@Nullable LongSparseArray<Object> result) {
|
|
if (array != null) {
|
|
if (result == null) {
|
|
result = new LongSparseArray<>();
|
|
}
|
|
final int accessSize = array.size();
|
|
for (int i = 0; i < accessSize; i++) {
|
|
result.put(array.keyAt(i), null);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/** @hide */
|
|
public static String uidStateToString(@UidState int uidState) {
|
|
switch (uidState) {
|
|
case UID_STATE_PERSISTENT: {
|
|
return "UID_STATE_PERSISTENT";
|
|
}
|
|
case UID_STATE_TOP: {
|
|
return "UID_STATE_TOP";
|
|
}
|
|
case UID_STATE_FOREGROUND_SERVICE_LOCATION: {
|
|
return "UID_STATE_FOREGROUND_SERVICE_LOCATION";
|
|
}
|
|
case UID_STATE_FOREGROUND_SERVICE: {
|
|
return "UID_STATE_FOREGROUND_SERVICE";
|
|
}
|
|
case UID_STATE_FOREGROUND: {
|
|
return "UID_STATE_FOREGROUND";
|
|
}
|
|
case UID_STATE_BACKGROUND: {
|
|
return "UID_STATE_BACKGROUND";
|
|
}
|
|
case UID_STATE_CACHED: {
|
|
return "UID_STATE_CACHED";
|
|
}
|
|
default: {
|
|
return "UNKNOWN";
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @hide */
|
|
public static int parseHistoricalMode(@NonNull String mode) {
|
|
switch (mode) {
|
|
case "HISTORICAL_MODE_ENABLED_ACTIVE": {
|
|
return HISTORICAL_MODE_ENABLED_ACTIVE;
|
|
}
|
|
case "HISTORICAL_MODE_ENABLED_PASSIVE": {
|
|
return HISTORICAL_MODE_ENABLED_PASSIVE;
|
|
}
|
|
default: {
|
|
return HISTORICAL_MODE_DISABLED;
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @hide */
|
|
public static String historicalModeToString(@HistoricalMode int mode) {
|
|
switch (mode) {
|
|
case HISTORICAL_MODE_DISABLED: {
|
|
return "HISTORICAL_MODE_DISABLED";
|
|
}
|
|
case HISTORICAL_MODE_ENABLED_ACTIVE: {
|
|
return "HISTORICAL_MODE_ENABLED_ACTIVE";
|
|
}
|
|
case HISTORICAL_MODE_ENABLED_PASSIVE: {
|
|
return "HISTORICAL_MODE_ENABLED_PASSIVE";
|
|
}
|
|
default: {
|
|
return "UNKNOWN";
|
|
}
|
|
}
|
|
}
|
|
|
|
private static int getSystemAlertWindowDefault() {
|
|
final Context context = ActivityThread.currentApplication();
|
|
if (context == null) {
|
|
return AppOpsManager.MODE_DEFAULT;
|
|
}
|
|
|
|
// system alert window is disable on low ram phones starting from Q
|
|
final PackageManager pm = context.getPackageManager();
|
|
if (null == pm) {
|
|
return AppOpsManager.MODE_DEFAULT;
|
|
}
|
|
// TVs are constantly plugged in and has less concern for memory/power
|
|
if (ActivityManager.isLowRamDeviceStatic()
|
|
&& !pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK, 0)) {
|
|
return AppOpsManager.MODE_IGNORED;
|
|
}
|
|
|
|
return AppOpsManager.MODE_DEFAULT;
|
|
}
|
|
|
|
/**
|
|
* Calculate left circular distance for two numbers modulo size.
|
|
* @hide
|
|
*/
|
|
public static int leftCircularDistance(int from, int to, int size) {
|
|
return (to + size - from) % size;
|
|
}
|
|
|
|
/**
|
|
* Helper method for noteOp, startOp and noteProxyOp to call AppOpsService to collect/log
|
|
* stack traces
|
|
*
|
|
* <p> For each call, the stacktrace op code, package name and long version code will be
|
|
* passed along where it will be logged/collected
|
|
*
|
|
* @param op The operation to note
|
|
*/
|
|
private void collectNoteOpCallsForValidation(int op) {
|
|
if (NOTE_OP_COLLECTION_ENABLED) {
|
|
try {
|
|
mService.collectNoteOpCallsForValidation(getFormattedStackTrace(),
|
|
op, mContext.getOpPackageName(), mContext.getApplicationInfo().longVersionCode);
|
|
} catch (RemoteException e) {
|
|
// Swallow error, only meant for logging ops, should not affect flow of the code
|
|
}
|
|
}
|
|
}
|
|
|
|
private static List<AttributedOpEntry> deduplicateDiscreteEvents(List<AttributedOpEntry> list) {
|
|
int n = list.size();
|
|
int i = 0;
|
|
for (int j = 0, k = 0; j < n; i++, j = k) {
|
|
long currentAccessTime = list.get(j).getLastAccessTime(OP_FLAGS_ALL);
|
|
k = j + 1;
|
|
while(k < n && list.get(k).getLastAccessTime(OP_FLAGS_ALL) == currentAccessTime) {
|
|
k++;
|
|
}
|
|
list.set(i, mergeAttributedOpEntries(list.subList(j, k)));
|
|
}
|
|
for (; i < n; i++) {
|
|
list.remove(list.size() - 1);
|
|
}
|
|
return list;
|
|
}
|
|
|
|
private static AttributedOpEntry mergeAttributedOpEntries(List<AttributedOpEntry> opEntries) {
|
|
if (opEntries.size() == 1) {
|
|
return opEntries.get(0);
|
|
}
|
|
LongSparseArray<AppOpsManager.NoteOpEvent> accessEvents = new LongSparseArray<>();
|
|
LongSparseArray<AppOpsManager.NoteOpEvent> rejectEvents = new LongSparseArray<>();
|
|
int opCount = opEntries.size();
|
|
for (int i = 0; i < opCount; i++) {
|
|
AttributedOpEntry a = opEntries.get(i);
|
|
ArraySet<Long> keys = a.collectKeys();
|
|
final int keyCount = keys.size();
|
|
for (int k = 0; k < keyCount; k++) {
|
|
final long key = keys.valueAt(k);
|
|
|
|
final int uidState = extractUidStateFromKey(key);
|
|
final int flags = extractFlagsFromKey(key);
|
|
|
|
NoteOpEvent access = a.getLastAccessEvent(uidState, uidState, flags);
|
|
NoteOpEvent reject = a.getLastRejectEvent(uidState, uidState, flags);
|
|
|
|
if (access != null) {
|
|
NoteOpEvent existingAccess = accessEvents.get(key);
|
|
if (existingAccess == null || existingAccess.getDuration() == -1) {
|
|
accessEvents.append(key, access);
|
|
} else if (existingAccess.mProxy == null && access.mProxy != null ) {
|
|
existingAccess.mProxy = access.mProxy;
|
|
}
|
|
}
|
|
if (reject != null) {
|
|
rejectEvents.append(key, reject);
|
|
}
|
|
}
|
|
}
|
|
return new AttributedOpEntry(opEntries.get(0).mOp, false, accessEvents, rejectEvents);
|
|
}
|
|
}
|