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

Commit a0104a05 authored by Mark Fasheh's avatar Mark Fasheh
Browse files

Add an ActivityManager callback for UID frozen state changes

Allow a client to ask for frozen state changes.  CachedAppOptimzer will
fire this callback when all processes in a Uid have become frozen and
also when a process in a fozen Uid becomes unfrozen.  Currently this is
intended to be used in ConnectivityService to kill sockets for frozen apps.

Freezing is done on a process basis however the callback operates on the
Uid level.  I added some methods and a boolean on UidRecord to query the
frozen state of it's proceses so that we only fire the callback when
appropriate.

This feature is off by default.

Test: Tested with a ConnectivityService patch to register for this callback
Test: and kill sockets on notification of a frozen Uid.
Bug: 253914114
Change-Id: I27ae40f345532836bdcb0e0dc3d7d7315c110d29
parent acfdabf2
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -18,10 +18,18 @@ package android.app {

  public class ActivityManager {
    method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void addHomeVisibilityListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.HomeVisibilityListener);
    method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void registerUidFrozenStateChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.app.ActivityManager.UidFrozenStateChangedCallback);
    method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void removeHomeVisibilityListener(@NonNull android.app.HomeVisibilityListener);
    method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void unregisterUidFrozenStateChangedCallback(@NonNull android.app.ActivityManager.UidFrozenStateChangedCallback);
    method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public boolean updateMccMncConfiguration(@NonNull String, @NonNull String);
  }

  public static interface ActivityManager.UidFrozenStateChangedCallback {
    method public void onUidFrozenStateChanged(@NonNull int[], @NonNull int[]);
    field public static final int UID_FROZEN_STATE_FROZEN = 1; // 0x1
    field public static final int UID_FROZEN_STATE_UNFROZEN = 2; // 0x2
  }

  public class AppOpsManager {
    field public static final String OPSTR_NO_ISOLATED_STORAGE = "android:no_isolated_storage";
  }
+127 −0
Original line number Diff line number Diff line
@@ -230,6 +230,133 @@ public class ActivityManager {

    final ArrayMap<OnUidImportanceListener, UidObserver> mImportanceListeners = new ArrayMap<>();

    /**
     * Map of callbacks that have registered for {@link UidFrozenStateChanged} events.
     * Will be called when a Uid has become frozen or unfrozen.
     */
    final ArrayMap<UidFrozenStateChangedCallback, Executor> mFrozenStateChangedCallbacks =
             new ArrayMap<>();

    private final IUidFrozenStateChangedCallback mFrozenStateChangedCallback =
            new IUidFrozenStateChangedCallback.Stub() {
            @Override
            public void onUidFrozenStateChanged(int[] uids, int[] frozenStates) {
                synchronized (mFrozenStateChangedCallbacks) {
                    mFrozenStateChangedCallbacks.forEach((callback, executor) -> {
                        executor.execute(
                                () -> callback.onUidFrozenStateChanged(uids, frozenStates));
                    });
                }
            }
        };

    /**
     * Callback object for {@link #registerUidFrozenStateChangedCallback}
     *
     * @hide
     */
    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
    public interface UidFrozenStateChangedCallback {
        /**
         * Indicates that the UID was frozen.
         *
         * @hide
         */
        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
        int UID_FROZEN_STATE_FROZEN = 1;

        /**
         * Indicates that the UID was unfrozen.
         *
         * @hide
         */
        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
        int UID_FROZEN_STATE_UNFROZEN = 2;

        /**
         * @hide
         */
        @Retention(RetentionPolicy.SOURCE)
        @IntDef(flag = false, prefix = {"UID_FROZEN_STATE_"}, value = {
                UID_FROZEN_STATE_FROZEN,
                UID_FROZEN_STATE_UNFROZEN,
        })
        public @interface UidFrozenState {}

        /**
         * @param uids The UIDs for which the frozen state has changed
         * @param frozenStates Frozen state for each UID index, Will be set to
         *               {@link UidFrozenStateChangedCallback#UID_FROZEN_STATE_FROZEN}
         *               when the UID is frozen. When the UID is unfrozen,
         *               {@link UidFrozenStateChangedCallback#UID_FROZEN_STATE_UNFROZEN}
         *               will be set.
         *
         * @hide
         */
        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
        void onUidFrozenStateChanged(@NonNull int[] uids,
                @NonNull @UidFrozenState int[] frozenStates);
    }

    /**
     * Register a {@link UidFrozenStateChangedCallback} object to receive notification
     * when a UID is frozen or unfrozen. Will throw an exception if the same
     * callback object is registered more than once.
     *
     * @param executor The executor that the callback will be run from.
     * @param callback The callback to be registered. Callbacks for previous frozen/unfrozen
     *                 UID changes will not be delivered. Only changes in state from the point of
     *                 registration onward will be reported.
     * @throws IllegalStateException if the {@code callback} is already registered.
     *
     * @hide
     */
    @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS)
    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
    public void registerUidFrozenStateChangedCallback(
            @NonNull Executor executor,
            @NonNull UidFrozenStateChangedCallback callback) {
        synchronized (mFrozenStateChangedCallbacks) {
            if (mFrozenStateChangedCallbacks.containsKey(callback)) {
                throw new IllegalArgumentException("Callback already registered: " + callback);
            }
            mFrozenStateChangedCallbacks.put(callback, executor);
            if (mFrozenStateChangedCallbacks.size() > 1) {
                /* There's no need to register more than one binder interface */
                return;
            }

            try {
                getService().registerUidFrozenStateChangedCallback(mFrozenStateChangedCallback);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
    }

    /**
     * Unregister a {@link UidFrozenStateChangedCallback} callback.
     * @param callback The callback to be unregistered.
     *
     * @hide
     */
    @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS)
    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
    public void unregisterUidFrozenStateChangedCallback(
            @NonNull UidFrozenStateChangedCallback callback) {
        synchronized (mFrozenStateChangedCallbacks) {
            mFrozenStateChangedCallbacks.remove(callback);
            if (mFrozenStateChangedCallbacks.isEmpty()) {
                try {
                    getService().unregisterUidFrozenStateChangedCallback(
                            mFrozenStateChangedCallback);
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
        }
    }

    /**
     * <a href="{@docRoot}guide/topics/manifest/meta-data-element.html">{@code
     * <meta-data>}</a> name for a 'home' Activity that declares a package that is to be
+4 −0
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import android.app.IServiceConnection;
import android.app.IStopUserCallback;
import android.app.ITaskStackListener;
import android.app.IUiAutomationConnection;
import android.app.IUidFrozenStateChangedCallback;
import android.app.IUidObserver;
import android.app.IUserSwitchObserver;
import android.app.Notification;
@@ -877,4 +878,7 @@ interface IActivityManager {

    /** Logs API state change to associate with an FGS, used for FGS Type Metrics */
    void logFgsApiStateChanged(int apiType, int state, int appUid, int appPid);

    void registerUidFrozenStateChangedCallback(in IUidFrozenStateChangedCallback callback);
    void unregisterUidFrozenStateChangedCallback(in IUidFrozenStateChangedCallback callback);
}
+25 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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;

/** {@hide} */
oneway interface IUidFrozenStateChangedCallback {
    /**
     * Report a new frozen state for the Uid list.
     */
    void onUidFrozenStateChanged(in int[] uids, in int[] frozenStates);
}
+63 −0
Original line number Diff line number Diff line
@@ -176,6 +176,7 @@ import android.app.ActivityManager.PendingIntentInfo;
import android.app.ActivityManager.ProcessCapability;
import android.app.ActivityManager.RestrictionLevel;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.UidFrozenStateChangedCallback.UidFrozenState;
import android.app.ActivityManagerInternal;
import android.app.ActivityManagerInternal.BindServiceEventListener;
import android.app.ActivityManagerInternal.BroadcastEventListener;
@@ -208,6 +209,7 @@ import android.app.IServiceConnection;
import android.app.IStopUserCallback;
import android.app.ITaskStackListener;
import android.app.IUiAutomationConnection;
import android.app.IUidFrozenStateChangedCallback;
import android.app.IUidObserver;
import android.app.IUnsafeIntentStrictModeCallback;
import android.app.IUserSwitchObserver;
@@ -316,6 +318,7 @@ import android.os.PowerManager.ServiceType;
import android.os.PowerManagerInternal;
import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
@@ -7757,6 +7760,66 @@ public class ActivityManagerService extends IActivityManager.Stub
        return uidRecord != null && !uidRecord.isSetIdle();
    }
    @GuardedBy("mUidFrozenStateChangedCallbackList")
    private final RemoteCallbackList<IUidFrozenStateChangedCallback>
            mUidFrozenStateChangedCallbackList = new RemoteCallbackList<>();
    /**
     * Register a {@link IUidFrozenStateChangedCallback} to receive Uid frozen state events.
     *
     * @param callback remote callback object to be registered
     */
    public void registerUidFrozenStateChangedCallback(
            @NonNull IUidFrozenStateChangedCallback callback) {
        synchronized (mUidFrozenStateChangedCallbackList) {
            boolean registered = mUidFrozenStateChangedCallbackList.register(callback);
            if (!registered) {
                Slog.w(TAG, "Failed to register with RemoteCallbackList!");
            }
        }
    }
    /**
     * Unregister a {@link IUidFrozenStateChangedCallback}.
     *
     * @param callback remote callback object to be unregistered
     */
    public void unregisterUidFrozenStateChangedCallback(
            @NonNull IUidFrozenStateChangedCallback callback) {
        synchronized (mUidFrozenStateChangedCallbackList) {
            mUidFrozenStateChangedCallbackList.unregister(callback);
        }
    }
    /**
     * Notify the system that a UID has been frozen or unfrozen.
     *
     * @param uids The Uid(s) in question
     * @param frozenStates Frozen state for each UID index
     *
     * @hide
     */
    public void reportUidFrozenStateChanged(@NonNull int[] uids,
            @UidFrozenState int[] frozenStates) {
        synchronized (mUidFrozenStateChangedCallbackList) {
            final int n = mUidFrozenStateChangedCallbackList.beginBroadcast();
            for (int i = 0; i < n; i++) {
                try {
                    mUidFrozenStateChangedCallbackList.getBroadcastItem(i).onUidFrozenStateChanged(
                            uids, frozenStates);
                } catch (RemoteException e) {
                    /*
                    * The process at the other end has died or otherwise gone away.
                    * According to spec, RemoteCallbacklist will take care of unregistering any
                    * object associated with that process - we are safe to ignore the exception
                    * here.
                    */
                }
            }
            mUidFrozenStateChangedCallbackList.finishBroadcast();
        }
    }
    @Override
    public void setPersistentVrThread(int tid) {
        mActivityTaskManager.setPersistentVrThread(tid);
Loading