• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.example.android.vdmdemo.host;
18 
19 import android.companion.virtual.VirtualDeviceManager.ActivityListener;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.pm.PackageManager;
23 import android.content.pm.PackageManager.NameNotFoundException;
24 import android.util.Log;
25 
26 import androidx.annotation.GuardedBy;
27 import androidx.annotation.NonNull;
28 
29 import java.util.Collection;
30 import java.util.Collections;
31 import java.util.HashMap;
32 import java.util.HashSet;
33 import java.util.Map;
34 import java.util.Objects;
35 import java.util.Optional;
36 import java.util.Set;
37 import java.util.function.Consumer;
38 import java.util.stream.Collectors;
39 
40 final class RunningVdmUidsTracker implements ActivityListener {
41     private static final String TAG = RunningVdmUidsTracker.class.getSimpleName();
42 
43     private final PackageManager mPackageManager;
44     private final PreferenceController mPreferenceController;
45     private final AudioStreamer mAudioStreamer;
46     private final AudioInjector mAudioInjector;
47 
48     private final Object mLock = new Object();
49 
50     @GuardedBy("mLock")
51     private final HashMap<Integer, HashSet<Integer>> mDisplayIdToRunningUids = new HashMap<>();
52 
53     @GuardedBy("mLock")
54     private Set<Integer> mRunningVdmUids = Collections.emptySet();
55 
RunningVdmUidsTracker(@onNull Context context, @NonNull PreferenceController preferenceController, @NonNull AudioStreamer audioStreamer, @NonNull AudioInjector audioInjector)56     RunningVdmUidsTracker(@NonNull Context context,
57             @NonNull PreferenceController preferenceController,
58             @NonNull AudioStreamer audioStreamer,
59             @NonNull AudioInjector audioInjector) {
60         mPackageManager = Objects.requireNonNull(context).getPackageManager();
61         mPreferenceController = Objects.requireNonNull(preferenceController);
62         mAudioStreamer = Objects.requireNonNull(audioStreamer);
63         mAudioInjector = Objects.requireNonNull(audioInjector);
64 
65         mPreferenceController.addPreferenceObserver(this, createPreferenceObserver());
66     }
67 
createPreferenceObserver()68     private Map<Integer, Consumer<Object>> createPreferenceObserver() {
69         HashMap<Integer, Consumer<Object>> observers = new HashMap<>();
70 
71         observers.put(R.string.pref_enable_client_audio, b -> {
72             synchronized (mLock) {
73                 updateVdmUids(mRunningVdmUids);
74             }
75         });
76 
77         return observers;
78     }
79 
80     @Override
onTopActivityChanged(int displayId, @NonNull ComponentName componentName)81     public void onTopActivityChanged(int displayId, @NonNull ComponentName componentName) {
82 
83         Optional<Integer> topActivityUid = getUidForComponent(componentName);
84         if (topActivityUid.isEmpty()) {
85             Log.w(TAG, "Cannot determine UID for top activity component " + componentName);
86             return;
87         }
88 
89         final Set<Integer> updatedUids;
90         synchronized (mLock) {
91             HashSet<Integer> displayUidSet =
92                     mDisplayIdToRunningUids.computeIfAbsent(displayId, k -> new HashSet<>());
93             displayUidSet.add(topActivityUid.get());
94             mRunningVdmUids =
95                     mDisplayIdToRunningUids.values().stream()
96                             .flatMap(Collection::stream)
97                             .collect(Collectors.toSet());
98             updatedUids = mRunningVdmUids;
99         }
100 
101         updateVdmUids(updatedUids);
102     }
103 
104     @Override
onDisplayEmpty(int displayId)105     public void onDisplayEmpty(int displayId) {
106         Set<Integer> uidsBefore;
107         Set<Integer> uidsAfter;
108         synchronized (mLock) {
109             uidsBefore = mRunningVdmUids;
110             mDisplayIdToRunningUids.remove(displayId);
111             mRunningVdmUids =
112                     mDisplayIdToRunningUids.values().stream()
113                             .flatMap(Collection::stream)
114                             .collect(Collectors.toSet());
115             uidsAfter = mRunningVdmUids;
116         }
117 
118         if (!uidsAfter.equals(uidsBefore)) {
119             updateVdmUids(uidsAfter);
120         }
121     }
122 
updateVdmUids(Set<Integer> uids)123     private void updateVdmUids(Set<Integer> uids) {
124         if (mPreferenceController.getBoolean(R.string.pref_enable_client_audio)) {
125             mAudioStreamer.updateVdmUids(uids);
126             mAudioInjector.updateVdmUids(uids);
127         }
128     }
129 
getUidForComponent(@onNull ComponentName topActivity)130     private Optional<Integer> getUidForComponent(@NonNull ComponentName topActivity) {
131         try {
132             return Optional.of(
133                     mPackageManager.getPackageUid(topActivity.getPackageName(), /* flags= */ 0));
134         } catch (NameNotFoundException e) {
135             return Optional.empty();
136         }
137     }
138 }
139