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

Commit b7b5ef1f authored by Kweku Adams's avatar Kweku Adams Committed by Android (Google) Code Review
Browse files

Merge "Flesh out the IRS some more."

parents 19584c63 43288412
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -53,7 +53,7 @@ interface EconomyManagerInternal {
     * Return {@code true} if the app has a balance equal to or greater than the specified min
     * balance.
     */
    boolean hasBalanceAtLeast(int userId, @NonNull String pkgName, double minBalance);
    boolean hasBalanceAtLeast(int userId, @NonNull String pkgName, int minBalance);

    /**
     * Note that an instantaneous event has occurred. The event must be specified in one of the
+285 −1
Original line number Diff line number Diff line
@@ -17,10 +17,32 @@
package com.android.server.tare;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.BatteryManagerInternal;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
import android.util.SparseSetArray;

import com.android.internal.annotations.GuardedBy;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.tare.EconomyManagerInternal.BalanceChangeListener;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * Responsible for handling app's ARC count based on events, ensuring ARCs are credited when
@@ -33,11 +55,82 @@ import com.android.server.SystemService;
 */
public class InternalResourceService extends SystemService {
    public static final String TAG = "TARE-IRS";
    public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
    public static final boolean DEBUG = Log.isLoggable("TARE", Log.DEBUG);

    /** Global local for all resource economy state. */
    private final Object mLock = new Object();

    private final Handler mHandler;
    private final BatteryManagerInternal mBatteryManagerInternal;
    private final PackageManager mPackageManager;

    @GuardedBy("mLock")
    private final CopyOnWriteArraySet<BalanceChangeListener> mBalanceChangeListeners =
            new CopyOnWriteArraySet<>();

    @NonNull
    private List<PackageInfo> mPkgCache = new ArrayList<>();

    /** Cached mapping of UIDs (for all users) to a list of packages in the UID. */
    private final SparseSetArray<String> mUidToPackageCache = new SparseSetArray<>();

    @GuardedBy("mLock")
    private boolean mIsSetup;
    // In the range [0,100] to represent 0% to 100% battery.
    @GuardedBy("mLock")
    private int mCurrentBatteryLevel;

    @SuppressWarnings("FieldCanBeLocal")
    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Nullable
        private String getPackageName(Intent intent) {
            Uri uri = intent.getData();
            return uri != null ? uri.getSchemeSpecificPart() : null;
        }

        @Override
        public void onReceive(Context context, Intent intent) {
            switch (intent.getAction()) {
                case Intent.ACTION_BATTERY_LEVEL_CHANGED:
                    onBatteryLevelChanged();
                    break;
                case Intent.ACTION_PACKAGE_FULLY_REMOVED: {
                    final String pkgName = getPackageName(intent);
                    final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
                    onPackageRemoved(pkgUid, pkgName);
                }
                break;
                case Intent.ACTION_PACKAGE_ADDED: {
                    if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
                        final String pkgName = getPackageName(intent);
                        final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
                        onPackageAdded(pkgUid, pkgName);
                    }
                }
                break;
                case Intent.ACTION_PACKAGE_RESTARTED: {
                    final String pkgName = getPackageName(intent);
                    final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
                    final int userId = UserHandle.getUserId(pkgUid);
                    onPackageForceStopped(userId, pkgName);
                }
                break;
                case Intent.ACTION_USER_ADDED: {
                    final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
                    onUserAdded(userId);
                }
                break;
                case Intent.ACTION_USER_REMOVED: {
                    final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
                    onUserRemoved(userId);
                }
                break;
            }
        }
    };

    private static final int MSG_NOTIFY_BALANCE_CHANGE_LISTENERS = 0;

    /**
     * Initializes the system service.
     * <p>
@@ -49,6 +142,25 @@ public class InternalResourceService extends SystemService {
     */
    public InternalResourceService(Context context) {
        super(context);

        mHandler = new IrsHandler(TareHandlerThread.get().getLooper());
        mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class);
        mPackageManager = context.getPackageManager();

        final IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_BATTERY_LEVEL_CHANGED);
        context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
        final IntentFilter pkgFilter = new IntentFilter();
        pkgFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
        pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
        pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
        pkgFilter.addDataScheme("package");
        context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, pkgFilter, null, null);
        final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
        userFilter.addAction(Intent.ACTION_USER_ADDED);
        context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);

        publishLocalService(EconomyManagerInternal.class, new LocalService());
    }

    @Override
@@ -60,4 +172,176 @@ public class InternalResourceService extends SystemService {
    Object getLock() {
        return mLock;
    }

    @NonNull
    List<PackageInfo> getInstalledPackages() {
        synchronized (mLock) {
            return mPkgCache;
        }
    }

    @Nullable
    @GuardedBy("mLock")
    ArraySet<String> getPackagesForUidLocked(final int uid) {
        ArraySet<String> packages = mUidToPackageCache.get(uid);
        if (packages == null) {
            String[] pkgs = mPackageManager.getPackagesForUid(uid);
            if (pkgs != null) {
                for (String pkg : pkgs) {
                    mUidToPackageCache.add(uid, pkg);
                }
                packages = mUidToPackageCache.get(uid);
            }
        }
        return packages;
    }

    void onAppStateChanged(final int userId, @NonNull final ArraySet<String> pkgNames) {
    }

    void onBatteryLevelChanged() {
        synchronized (mLock) {
            final int newBatteryLevel = getCurrentBatteryLevel();
            mCurrentBatteryLevel = newBatteryLevel;
        }
    }

    void onDeviceStateChanged() {
    }

    void onPackageAdded(final int uid, @NonNull final String pkgName) {
        final int userId = UserHandle.getUserId(uid);
        final PackageInfo packageInfo;
        try {
            packageInfo = mPackageManager.getPackageInfoAsUser(pkgName, 0, userId);
        } catch (PackageManager.NameNotFoundException e) {
            Slog.wtf(TAG, "PM couldn't find newly added package: " + pkgName);
            return;
        }
        synchronized (mLock) {
            mPkgCache.add(packageInfo);
            mUidToPackageCache.add(uid, pkgName);
        }
    }

    void onPackageForceStopped(final int userId, @NonNull final String pkgName) {
        synchronized (mLock) {
            // TODO: reduce ARC count by some amount
        }
    }

    void onPackageRemoved(final int uid, @NonNull final String pkgName) {
        final int userId = UserHandle.getUserId(uid);
        synchronized (mLock) {
            mUidToPackageCache.remove(uid, pkgName);
            for (int i = 0; i < mPkgCache.size(); ++i) {
                PackageInfo pkgInfo = mPkgCache.get(i);
                if (UserHandle.getUserId(pkgInfo.applicationInfo.uid) == userId
                        && pkgName.equals(pkgInfo.packageName)) {
                    mPkgCache.remove(i);
                    break;
                }
            }
        }
    }

    void onUserAdded(final int userId) {
        synchronized (mLock) {
            loadInstalledPackageListLocked();
        }
    }

    void onUserRemoved(final int userId) {
        synchronized (mLock) {
            ArrayList<String> removedPkgs = new ArrayList<>();
            for (int i = mPkgCache.size() - 1; i >= 0; --i) {
                PackageInfo pkgInfo = mPkgCache.get(i);
                if (UserHandle.getUserId(pkgInfo.applicationInfo.uid) == userId) {
                    removedPkgs.add(pkgInfo.packageName);
                    mUidToPackageCache.remove(pkgInfo.applicationInfo.uid);
                    mPkgCache.remove(i);
                    break;
                }
            }
            loadInstalledPackageListLocked();
        }
    }

    void postSolvencyChanged(final int userId, @NonNull final String pkgName, boolean nowSolvent) {
        mHandler.obtainMessage(
                MSG_NOTIFY_BALANCE_CHANGE_LISTENERS, userId, nowSolvent ? 1 : 0, pkgName)
                .sendToTarget();
    }

    private int getCurrentBatteryLevel() {
        return mBatteryManagerInternal.getBatteryLevel();
    }

    @GuardedBy("mLock")
    private void loadInstalledPackageListLocked() {
        mPkgCache = mPackageManager.getInstalledPackages(0);
    }

    private void setupEconomy() {
        synchronized (mLock) {
            loadInstalledPackageListLocked();
            mIsSetup = true;
        }
    }

    private final class IrsHandler extends Handler {
        IrsHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_NOTIFY_BALANCE_CHANGE_LISTENERS:
                    final int userId = msg.arg1;
                    final String pkgName = (String) msg.obj;
                    final boolean nowSolvent = msg.arg2 == 1;
                    for (BalanceChangeListener listener : mBalanceChangeListeners) {
                        if (nowSolvent) {
                            listener.onSolvent(userId, pkgName);
                        } else {
                            listener.onBankruptcy(userId, pkgName);
                        }
                    }
                    break;
            }
        }
    }

    // TODO: implement
    private final class LocalService implements EconomyManagerInternal {

        @Override
        public void registerBalanceChangeListener(@NonNull BalanceChangeListener listener) {
        }

        @Override
        public void unregisterBalanceChangeListener(@NonNull BalanceChangeListener listener) {
        }

        @Override
        public boolean hasBalanceAtLeast(int userId, @NonNull String pkgName, int minBalance) {
            return false;
        }

        @Override
        public void noteInstantaneousEvent(int userId, @NonNull String pkgName,
                @NonNull String event, @Nullable String tag) {
        }

        @Override
        public void noteOngoingEventStarted(int userId, @NonNull String pkgName,
                @NonNull String event, @Nullable String tag) {
        }

        @Override
        public void noteOngoingEventStopped(int userId, @NonNull String pkgName,
                @NonNull String event, @Nullable String tag) {
        }
    }
}