Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 90516b92 authored by Stanislav Zholnin's avatar Stanislav Zholnin
Browse files

Stack Trace sampling and reporting.

Sampling is done in cycles where length of single cycle is
configurable server side.
During one sampling cycle only one stack trace / message is reported.

Two sampling strategies are used in this CL.

1. Sampling uniformly packages and appops, and waiting only for
messages coming from sampled package (all other packages are
silenced to presereve system resources). All appops are recorded,
but sampled appop has preference and replaces any previous stack
trace / message. This strategy is created to focus on all packages
and all apps uniformly.

2. Creating list of rarely used packages - packages which were not
using any dangerous permissions for more than a week. Any newly
installed app is automatically added to this list, including apps
which were updated. Whenever message is received for rarely used
package, there is 50% probability that this message will replace
whatever is being sampled in current sampling cycle. This strategy
is created to focus on newly installed apps, on apps which are used
infrequently and on apps which use dangerous permissions infrequently.

Bug:136134050
Test: atest android.app.appops.cts.RuntimeMessageCollectionTest
Change-Id: I3109b38bf0482481acf945d5441a26bfe704c9b5
parent f11642db
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -6426,9 +6426,13 @@ package android.app {
  public class StatusBarManager {
  }
  public final class SyncNotedAppOp {
  public final class SyncNotedAppOp implements android.os.Parcelable {
    ctor public SyncNotedAppOp(@IntRange(from=0L) int, @Nullable String);
    method public int describeContents();
    method @Nullable public String getFeatureId();
    method @NonNull public String getOp();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.app.SyncNotedAppOp> CREATOR;
  }
  @Deprecated public class TabActivity extends android.app.ActivityGroup {
+14 −0
Original line number Diff line number Diff line
@@ -364,6 +364,7 @@ package android.app {
  }
  public class AppOpsManager {
    method @Nullable @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public android.app.RuntimeAppOpAccessMessage collectRuntimeAppOpAccessMessage();
    method @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public void getHistoricalOps(@NonNull android.app.AppOpsManager.HistoricalOpsRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
    method public static String[] getOpStrs();
    method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, @NonNull String, @Nullable java.lang.String...);
@@ -680,6 +681,19 @@ package android.app {
    method public void setNotificationAssistantAccessGranted(@Nullable android.content.ComponentName, boolean);
  }
  public final class RuntimeAppOpAccessMessage implements android.os.Parcelable {
    ctor public RuntimeAppOpAccessMessage(@IntRange(from=0L) int, @IntRange(from=0L) int, @NonNull String, @Nullable String, @NonNull String, int);
    method public int describeContents();
    method @Nullable public String getFeatureId();
    method @NonNull public String getMessage();
    method @NonNull public String getOp();
    method @NonNull public String getPackageName();
    method public int getSamplingStrategy();
    method @IntRange(from=0L) public int getUid();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.app.RuntimeAppOpAccessMessage> CREATOR;
  }
  public class SearchManager implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
    method public void launchAssist(@Nullable android.os.Bundle);
  }
+14 −0
Original line number Diff line number Diff line
@@ -164,6 +164,7 @@ package android.app {
  public class AppOpsManager {
    method @RequiresPermission("android.permission.MANAGE_APPOPS") public void addHistoricalOps(@NonNull android.app.AppOpsManager.HistoricalOps);
    method @RequiresPermission("android.permission.MANAGE_APPOPS") public void clearHistory();
    method @Nullable @RequiresPermission("android.permission.GET_APP_OPS_STATS") public android.app.RuntimeAppOpAccessMessage collectRuntimeAppOpAccessMessage();
    method @RequiresPermission("android.permission.GET_APP_OPS_STATS") public void getHistoricalOps(@NonNull android.app.AppOpsManager.HistoricalOpsRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
    method @RequiresPermission("android.permission.MANAGE_APPOPS") public void getHistoricalOpsFromDiskRaw(@NonNull android.app.AppOpsManager.HistoricalOpsRequest, @Nullable java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
    method public static int getNumOps();
@@ -447,6 +448,19 @@ package android.app {
    method public android.graphics.Rect getSourceRectHint();
  }

  public final class RuntimeAppOpAccessMessage implements android.os.Parcelable {
    ctor public RuntimeAppOpAccessMessage(@IntRange(from=0L) int, @IntRange(from=0L) int, @NonNull String, @Nullable String, @NonNull String, int);
    method public int describeContents();
    method @Nullable public String getFeatureId();
    method @NonNull public String getMessage();
    method @NonNull public String getOp();
    method @NonNull public String getPackageName();
    method public int getSamplingStrategy();
    method @IntRange(from=0L) public int getUid();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.app.RuntimeAppOpAccessMessage> CREATOR;
  }

  public class StatusBarManager {
    method public void collapsePanels();
    method public void expandNotificationsPanel();
+119 −3
Original line number Diff line number Diff line
@@ -16,6 +16,10 @@

package android.app;

import static android.util.StatsLogInternal.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__DEFAULT;
import static android.util.StatsLogInternal.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__RARELY_USED;
import static android.util.StatsLogInternal.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__UNIFORM;

import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
@@ -48,6 +52,8 @@ 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.UserManager;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -63,6 +69,7 @@ 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.MessageSamplingConfig;
import com.android.internal.os.RuntimeInit;
import com.android.internal.os.ZygoteInit;
import com.android.internal.util.ArrayUtils;
@@ -141,6 +148,13 @@ public class AppOpsManager {
    @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<>();
@@ -159,6 +173,50 @@ public class AppOpsManager {
    @GuardedBy("sLock")
    private static @Nullable AppOpsCollector sNotedAppOpsCollector;

    /**
     * Additional collector that collect accesses and forwards a few of them them via
     * {@link IAppOpsService#reportRuntimeAppOpAccessMessageAndGetConfig}.
     */
    private static AppOpsCollector sMessageCollector =
            new AppOpsCollector() {
                @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 (sConfig.getSampledOpCode() == OP_NONE
                            && sConfig.getExpirationTimeSinceBootMillis()
                            >= SystemClock.elapsedRealtime()) {
                        return;
                    }

                    MessageSamplingConfig config = sConfig;
                    if (leftCircularDistance(strOpToOp(op.getOp()), config.getSampledOpCode(),
                            _NUM_OP) <= config.getAcceptableLeftDistance()
                            || config.getExpirationTimeSinceBootMillis()
                            < SystemClock.elapsedRealtime()) {
                        String stackTrace = getFormattedStackTrace();
                        try {
                            sConfig = getService().reportRuntimeAppOpAccessMessageAndGetConfig(
                                    ActivityThread.currentOpPackageName(), op, stackTrace);
                        } catch (RemoteException e) {
                            e.rethrowFromSystemServer();
                        }
                    }
                }
            };

    static IBinder sClientId;

    /**
@@ -550,7 +608,6 @@ public class AppOpsManager {
    })
    public @interface OpFlags {}


    /** @hide */
    public static final String getFlagName(@OpFlags int flag) {
        switch (flag) {
@@ -569,6 +626,18 @@ public class AppOpsManager {
        }
    }

    /**
     * Strategies used for message sampling
     * @hide
     */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(prefix = {"RUNTIME_APP_OPS_ACCESS__SAMPLING_STRATEGY__"}, value = {
            RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__DEFAULT,
            RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__UNIFORM,
            RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__RARELY_USED
    })
    public @interface SamplingStrategy {}

    private static final int UID_STATE_OFFSET = 31;
    private static final int FLAGS_MASK = 0xFFFFFFFF;

@@ -2225,6 +2294,10 @@ public class AppOpsManager {
        }
    }

    /** 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";

@@ -7268,6 +7341,17 @@ public class AppOpsManager {
        }
    }

    /** @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
     */
@@ -7614,6 +7698,7 @@ public class AppOpsManager {
                sNotedAppOpsCollector.onSelfNoted(new SyncNotedAppOp(op, featureId));
            }
        }
        sMessageCollector.onSelfNoted(new SyncNotedAppOp(op, featureId));
    }

    /**
@@ -7764,6 +7849,10 @@ public class AppOpsManager {
                        }
                    }
                }
                for (int code = notedAppOps.nextSetBit(0); code != -1;
                        code = notedAppOps.nextSetBit(code + 1)) {
                    sMessageCollector.onNoted(new SyncNotedAppOp(code, featureId));
                }
            }
        }
    }
@@ -7958,10 +8047,13 @@ public class AppOpsManager {

        StringBuilder sb = new StringBuilder();
        for (int i = firstInteresting; i <= lastInteresting; i++) {
            sb.append(trace[i]);
            if (i != lastInteresting) {
            if (i != firstInteresting) {
                sb.append('\n');
            }
            if (sb.length() + trace[i].toString().length() > 600) {
                break;
            }
            sb.append(trace[i]);
        }

        return sb.toString();
@@ -8088,6 +8180,22 @@ public class AppOpsManager {
        }
    }

    /**
     * Pulls current AppOps access report and picks package and op to watch for next access report
     *
     * @hide
     */
    @SystemApi
    @TestApi
    @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
@@ -8297,4 +8405,12 @@ public class AppOpsManager {

        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;
    }
}
+19 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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;

parcelable RuntimeAppOpAccessMessage;
Loading