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

Commit 3ee3a566 authored by Hui Yu's avatar Hui Yu Committed by Automerger Merge Worker
Browse files

Start/stop foreground service delegate for musicfx app. am: 05536405

parents 26f35f88 05536405
Loading
Loading
Loading
Loading
+26 −29
Original line number Diff line number Diff line
@@ -37,7 +37,6 @@ import static android.os.Process.INVALID_UID;
import static android.provider.Settings.Secure.VOLUME_HUSH_MUTE;
import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
import static android.provider.Settings.Secure.VOLUME_HUSH_VIBRATE;
import static com.android.server.audio.SoundDoseHelper.ACTION_CHECK_MUSIC_ACTIVE;
import static com.android.server.utils.EventLogger.Event.ALOGE;
import static com.android.server.utils.EventLogger.Event.ALOGI;
@@ -73,7 +72,6 @@ import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -263,6 +261,8 @@ public class AudioService extends IAudioService.Stub
    private final SettingsAdapter mSettings;
    private final AudioPolicyFacade mAudioPolicy;
    private final MusicFxHelper mMusicFxHelper;
    /** Debug audio mode */
    protected static final boolean DEBUG_MODE = false;
@@ -403,9 +403,15 @@ public class AudioService extends IAudioService.Stub
    private static final int MSG_CONFIGURATION_CHANGED = 54;
    private static final int MSG_BROADCAST_MASTER_MUTE = 55;
    /** Messages handled by the {@link SoundDoseHelper}. */
    /**
     * Messages handled by the {@link SoundDoseHelper}, do not exceed
     * {@link MUSICFX_HELPER_MSG_START}.
     */
    /*package*/ static final int SAFE_MEDIA_VOLUME_MSG_START = 1000;
    /** Messages handled by the {@link MusicFxHelper}. */
    /*package*/ static final int MUSICFX_HELPER_MSG_START = 1100;
    // start of messages handled under wakelock
    //   these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
    //   and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -1268,6 +1274,8 @@ public class AudioService extends IAudioService.Stub
                0 /* arg1 */, 0 /* arg2 */, null /* obj */, 0 /* delay */);
        queueMsgUnderWakeLock(mAudioHandler, MSG_INIT_SPATIALIZER,
                0 /* arg1 */, 0 /* arg2 */, null /* obj */, 0 /* delay */);
        mMusicFxHelper = new MusicFxHelper(mContext, mAudioHandler);
    }
    private void initVolumeStreamStates() {
@@ -9409,11 +9417,21 @@ public class AudioService extends IAudioService.Stub
                    onConfigurationChanged();
                    break;
                default:
                    if (msg.what >= SAFE_MEDIA_VOLUME_MSG_START) {
                        // msg could be for the SoundDoseHelper
                case MusicFxHelper.MSG_EFFECT_CLIENT_GONE:
                    mMusicFxHelper.handleMessage(msg);
                    break;
                case SoundDoseHelper.MSG_CONFIGURE_SAFE_MEDIA:
                case SoundDoseHelper.MSG_CONFIGURE_SAFE_MEDIA_FORCED:
                case SoundDoseHelper.MSG_PERSIST_SAFE_VOLUME_STATE:
                case SoundDoseHelper.MSG_PERSIST_MUSIC_ACTIVE_MS:
                case SoundDoseHelper.MSG_PERSIST_CSD_VALUES:
                case SoundDoseHelper.MSG_CSD_UPDATE_ATTENUATION:
                    mSoundDoseHelper.handleMessage(msg);
                    }
                    break;
                default:
                    Log.e(TAG, "Unsupported msgId " + msg.what);
            }
        }
    }
@@ -9648,7 +9666,7 @@ public class AudioService extends IAudioService.Stub
                }
            } else if (action.equals(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION) ||
                    action.equals(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION)) {
                handleAudioEffectBroadcast(context, intent);
                mMusicFxHelper.handleAudioEffectBroadcast(context, intent);
            } else if (action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
                final int[] suspendedUids = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
                final String[] suspendedPackages =
@@ -9703,27 +9721,6 @@ public class AudioService extends IAudioService.Stub
        }
    } // end class AudioServiceUserRestrictionsListener
    private void handleAudioEffectBroadcast(Context context, Intent intent) {
        String target = intent.getPackage();
        if (target != null) {
            Log.w(TAG, "effect broadcast already targeted to " + target);
            return;
        }
        intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
        // TODO this should target a user-selected panel
        List<ResolveInfo> ril = context.getPackageManager().queryBroadcastReceivers(
                intent, 0 /* flags */);
        if (ril != null && ril.size() != 0) {
            ResolveInfo ri = ril.get(0);
            if (ri != null && ri.activityInfo != null && ri.activityInfo.packageName != null) {
                intent.setPackage(ri.activityInfo.packageName);
                context.sendBroadcastAsUser(intent, UserHandle.ALL);
                return;
            }
        }
        Log.w(TAG, "couldn't find receiver package for effect intent");
    }
    private void killBackgroundUserProcessesWithRecordAudioPermission(UserInfo oldUser) {
        PackageManager pm = mContext.getPackageManager();
        // Find the home activity of the user. It should not be killed to avoid expensive restart,
+295 −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 com.android.server.audio;

import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static com.android.server.audio.AudioService.MUSICFX_HELPER_MSG_START;

import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.IUidObserver;
import android.app.UidObserver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.media.AudioManager;
import android.media.audiofx.AudioEffect;
import android.os.Binder;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
import android.util.SparseArray;

import com.android.internal.annotations.GuardedBy;
import com.android.server.audio.AudioService.AudioHandler;

import java.util.ArrayList;
import java.util.List;

/**
 * MusicFx management.
 * .
 */
public class MusicFxHelper {
    private static final String TAG = "AS.MusicFxHelper";

    @NonNull private final Context mContext;

    @NonNull private final AudioHandler mAudioHandler;

    // Synchronization UidSessionMap access between UidObserver and AudioServiceBroadcastReceiver.
    private final Object mClientUidMapLock = new Object();

    // The binder token identifying the UidObserver registration.
    private IBinder mUidObserverToken = null;

    // Hashmap of UID and list of open sessions for this UID.
    @GuardedBy("mClientUidMapLock")
    private SparseArray<List<Integer>> mClientUidSessionMap = new SparseArray<>();

    /*package*/ static final int MSG_EFFECT_CLIENT_GONE = MUSICFX_HELPER_MSG_START + 1;

    // UID observer for effect MusicFx clients
    private final IUidObserver mEffectUidObserver = new UidObserver() {
        @Override public void onUidGone(int uid, boolean disabled) {
            Log.w(TAG, " send MSG_EFFECT_CLIENT_GONE");
            mAudioHandler.sendMessageAtTime(
                    mAudioHandler.obtainMessage(MSG_EFFECT_CLIENT_GONE,
                            uid /* arg1 */, 0 /* arg2 */,
                            null /* obj */), 0 /* delay */);
        }
    };

    // BindService connection implementation, we don't need any implementation now
    private ServiceConnection mMusicFxBindConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d(TAG, " service connected to " + name);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.d(TAG, " service disconnected from " + name);
        }
    };

    MusicFxHelper(@NonNull Context context, @NonNull AudioHandler audioHandler) {
        mContext = context;
        mAudioHandler = audioHandler;
    }

    /**
     * Handle the broadcast {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} and
     * {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION} intents.
     *
     * If the intent is {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION}:
     *  - If the MusicFx process is not running, call bindService with AUTO_CREATE to create.
     *  - If this is the first audio session in MusicFx, call set foreground service delegate.
     *  - If this is the first audio session for a given UID, add the UID into observer.
     *
     * If the intent is {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION}:
     *  - MusicFx will not be foreground delegated anymore.
     *  - The KeepAliveService of MusicFx will be unbound.
     *  - The UidObserver will be removed.
     */
    public void handleAudioEffectBroadcast(Context context, Intent intent) {
        String target = intent.getPackage();
        if (target != null) {
            Log.w(TAG, "effect broadcast already targeted to " + target);
            return;
        }
        intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
        final PackageManager pm = context.getPackageManager();
        // TODO this should target a user-selected panel
        List<ResolveInfo> ril = pm.queryBroadcastReceivers(intent, 0 /* flags */);
        if (ril != null && ril.size() != 0) {
            ResolveInfo ri = ril.get(0);
            final String senderPackageName = intent.getStringExtra(AudioEffect.EXTRA_PACKAGE_NAME);
            try {
                final int senderUid = pm.getPackageUidAsUser(senderPackageName,
                        PackageManager.PackageInfoFlags.of(MATCH_ANY_USER), getCurrentUserId());
                if (ri != null && ri.activityInfo != null && ri.activityInfo.packageName != null) {
                    intent.setPackage(ri.activityInfo.packageName);
                    synchronized (mClientUidMapLock) {
                        setMusicFxServiceWithObserver(context, intent, senderUid);
                    }
                    context.sendBroadcastAsUser(intent, UserHandle.ALL);
                    return;
                }
            } catch (PackageManager.NameNotFoundException e) {
                Log.e(TAG, "Not able to find UID from package: " + senderPackageName + " error: "
                        + e);
            }
        }
        Log.w(TAG, "couldn't find receiver package for effect intent");
    }

    /**
     * Handle the UidObserver onUidGone callback of MusicFx clients.
     * All open audio sessions of this UID will be closed.
     * If this is the last UID for MusicFx:
     *  - MusicFx will not be foreground delegated anymore.
     *  - The KeepAliveService of MusicFx will be unbound.
     *  - The UidObserver will be removed.
     */
    public void handleEffectClientUidGone(int uid) {
        synchronized (mClientUidMapLock) {
            Log.w(TAG, " inside handle MSG_EFFECT_CLIENT_GONE");
            // Once the uid is no longer running, close all remain audio session(s) for this UID
            if (mClientUidSessionMap.get(Integer.valueOf(uid)) != null) {
                final List<Integer> sessions = mClientUidSessionMap.get(Integer.valueOf(uid));
                Log.i(TAG, "UID " + uid + " gone, closing " + sessions.size() + " sessions");
                for (Integer session : sessions) {
                    Intent intent = new Intent(
                            AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION);
                    intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, session);
                    setMusicFxServiceWithObserver(mContext, intent, uid);
                    Log.i(TAG, "Close session " + session + " of UID " + uid);
                }
                mClientUidSessionMap.remove(Integer.valueOf(uid));
            }
        }
    }

    @GuardedBy("mClientUidMapLock")
    private void setMusicFxServiceWithObserver(Context context, Intent intent, int senderUid) {
        PackageManager pm = context.getPackageManager();
        try {
            final int audioSession = intent.getIntExtra(AudioEffect.EXTRA_AUDIO_SESSION,
                    AudioManager.AUDIO_SESSION_ID_GENERATE);
            if (AudioManager.AUDIO_SESSION_ID_GENERATE == audioSession) {
                Log.e(TAG, "Intent missing audio session: " + audioSession);
                return;
            }

            // only apply to com.android.musicfx and KeepAliveService for now
            final String musicFxPackageName = "com.android.musicfx";
            final String musicFxKeepAliveService = "com.android.musicfx.KeepAliveService";
            final int musicFxUid = pm.getPackageUidAsUser(musicFxPackageName,
                    PackageManager.PackageInfoFlags.of(MATCH_ANY_USER), getCurrentUserId());

            if (intent.getAction().equals(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION)) {
                List<Integer> sessions = new ArrayList<>();
                Log.d(TAG, "UID " + senderUid + ", open MusicFx session " + audioSession);
                // start foreground service delegate and register UID observer with the first
                // session of first UID open
                if (0 == mClientUidSessionMap.size()) {
                    final int procState = ActivityManager.getService().getPackageProcessState(
                            musicFxPackageName, this.getClass().getPackage().getName());
                    // if musicfx process not in binding state, call bindService with AUTO_CREATE
                    if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
                        Intent bindIntent = new Intent().setClassName(musicFxPackageName,
                                musicFxKeepAliveService);
                        context.bindServiceAsUser(
                                bindIntent, mMusicFxBindConnection, Context.BIND_AUTO_CREATE,
                                UserHandle.of(getCurrentUserId()));
                        Log.i(TAG, "bindService to " + musicFxPackageName);
                    }

                    Log.i(TAG, "Package " + musicFxPackageName + " uid " + musicFxUid
                            + " procState " + procState);
                } else if (mClientUidSessionMap.get(Integer.valueOf(senderUid)) != null) {
                    sessions = mClientUidSessionMap.get(Integer.valueOf(senderUid));
                    if (sessions.contains(audioSession)) {
                        Log.e(TAG, "Audio session " + audioSession + " already exist for UID "
                                + senderUid + ", abort");
                        return;
                    }
                }
                // first session of this UID
                if (sessions.size() == 0) {
                    // call registerUidObserverForUids with the first UID and first session
                    if (mClientUidSessionMap.size() == 0 || mUidObserverToken == null) {
                        mUidObserverToken = ActivityManager.getService().registerUidObserverForUids(
                                mEffectUidObserver, ActivityManager.UID_OBSERVER_GONE,
                                ActivityManager.PROCESS_STATE_UNKNOWN, null, new int[]{senderUid});
                        Log.i(TAG, "UID " + senderUid + " registered to observer");
                    } else {
                        // add UID to observer for each new UID
                        ActivityManager.getService().addUidToObserver(mUidObserverToken, TAG,
                                senderUid);
                        Log.i(TAG, "UID " + senderUid + " addeded to observer");
                    }
                }

                sessions.add(Integer.valueOf(audioSession));
                mClientUidSessionMap.put(Integer.valueOf(senderUid), sessions);
            } else {
                if (mClientUidSessionMap.get(senderUid) != null) {
                    Log.d(TAG, "UID " + senderUid + ", close MusicFx session " + audioSession);
                    List<Integer> sessions = mClientUidSessionMap.get(Integer.valueOf(senderUid));
                    sessions.remove(Integer.valueOf(audioSession));
                    if (0 == sessions.size()) {
                        mClientUidSessionMap.remove(Integer.valueOf(senderUid));
                    } else {
                        mClientUidSessionMap.put(Integer.valueOf(senderUid), sessions);
                    }

                    // stop foreground service delegate and unregister UID observer with the
                    // last session of last UID close
                    if (0 == mClientUidSessionMap.size()) {
                        ActivityManager.getService().unregisterUidObserver(mEffectUidObserver);
                        mClientUidSessionMap.clear();
                        context.unbindService(mMusicFxBindConnection);
                        Log.i(TAG, " remove all sessions, unregister UID observer, and unbind "
                                + musicFxPackageName);
                    }
                } else {
                    // if the audio session already closed, print an error
                    Log.e(TAG, "UID " + senderUid + " close audio session " + audioSession
                            + " which does not exist");
                }
            }
        } catch (PackageManager.NameNotFoundException e) {
            Log.e(TAG, "Not able to find UID from package: " + e);
        } catch (RemoteException e) {
            Log.e(TAG, "RemoteException " + e + " with handling intent");
        }
    }

    private int getCurrentUserId() {
        final long ident = Binder.clearCallingIdentity();
        try {
            UserInfo currentUser = ActivityManager.getService().getCurrentUser();
            return currentUser.id;
        } catch (RemoteException e) {
            // Activity manager not running, nothing we can do assume user 0.
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
        return UserHandle.USER_SYSTEM;
    }

    /*package*/ void handleMessage(Message msg) {
        switch (msg.what) {
            case MSG_EFFECT_CLIENT_GONE:
                Log.w(TAG, " handle MSG_EFFECT_CLIENT_GONE");
                handleEffectClientUidGone(msg.arg1 /* uid */);
                break;
            default:
                Log.e(TAG, "Unexpected msg to handle in MusicFxHelper: " + msg.what);
                break;
        }
    }

}
+5 −5
Original line number Diff line number Diff line
@@ -108,11 +108,11 @@ public class SoundDoseHelper {
    private static final int SAFE_MEDIA_VOLUME_INACTIVE = 2;  // confirmed
    private static final int SAFE_MEDIA_VOLUME_ACTIVE = 3;  // unconfirmed

    private static final int MSG_CONFIGURE_SAFE_MEDIA = SAFE_MEDIA_VOLUME_MSG_START + 1;
    private static final int MSG_CONFIGURE_SAFE_MEDIA_FORCED = SAFE_MEDIA_VOLUME_MSG_START + 2;
    private static final int MSG_PERSIST_SAFE_VOLUME_STATE = SAFE_MEDIA_VOLUME_MSG_START + 3;
    private static final int MSG_PERSIST_MUSIC_ACTIVE_MS = SAFE_MEDIA_VOLUME_MSG_START + 4;
    private static final int MSG_PERSIST_CSD_VALUES = SAFE_MEDIA_VOLUME_MSG_START + 5;
    /*package*/ static final int MSG_CONFIGURE_SAFE_MEDIA = SAFE_MEDIA_VOLUME_MSG_START + 1;
    /*package*/ static final int MSG_CONFIGURE_SAFE_MEDIA_FORCED = SAFE_MEDIA_VOLUME_MSG_START + 2;
    /*package*/ static final int MSG_PERSIST_SAFE_VOLUME_STATE = SAFE_MEDIA_VOLUME_MSG_START + 3;
    /*package*/ static final int MSG_PERSIST_MUSIC_ACTIVE_MS = SAFE_MEDIA_VOLUME_MSG_START + 4;
    /*package*/ static final int MSG_PERSIST_CSD_VALUES = SAFE_MEDIA_VOLUME_MSG_START + 5;
    /*package*/ static final int MSG_CSD_UPDATE_ATTENUATION = SAFE_MEDIA_VOLUME_MSG_START + 6;

    private static final int UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX = (20 * 3600 * 1000); // 20 hours