• 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 com.google.common.collect.ImmutableSet;
30 
31 import java.util.Collection;
32 import java.util.Collections;
33 import java.util.HashMap;
34 import java.util.HashSet;
35 import java.util.Map;
36 import java.util.Objects;
37 import java.util.Optional;
38 import java.util.Set;
39 import java.util.function.Consumer;
40 import java.util.stream.Collectors;
41 
42 final class RunningVdmUidsTracker implements ActivityListener {
43     private static final String TAG = RunningVdmUidsTracker.class.getSimpleName();
44 
45     private final PackageManager mPackageManager;
46     private final PreferenceController mPreferenceController;
47     private final AudioStreamer mAudioStreamer;
48     private final AudioInjector mAudioInjector;
49 
50     private final Object mLock = new Object();
51 
52     @GuardedBy("mLock")
53     private final HashMap<Integer, HashSet<Integer>> mDisplayIdToRunningUids = new HashMap<>();
54 
55     @GuardedBy("mLock")
56     private Set<Integer> mRunningVdmUids = Collections.emptySet();
57 
RunningVdmUidsTracker(@onNull Context context, @NonNull PreferenceController preferenceController, @NonNull AudioStreamer audioStreamer, @NonNull AudioInjector audioInjector)58     RunningVdmUidsTracker(@NonNull Context context,
59             @NonNull PreferenceController preferenceController,
60             @NonNull AudioStreamer audioStreamer,
61             @NonNull AudioInjector audioInjector) {
62         mPackageManager = Objects.requireNonNull(context).getPackageManager();
63         mPreferenceController = Objects.requireNonNull(preferenceController);
64         mAudioStreamer = Objects.requireNonNull(audioStreamer);
65         mAudioInjector = Objects.requireNonNull(audioInjector);
66 
67         mPreferenceController.addPreferenceObserver(this, createPreferenceObserver());
68     }
69 
createPreferenceObserver()70     private Map<Integer, Consumer<Object>> createPreferenceObserver() {
71         HashMap<Integer, Consumer<Object>> observers = new HashMap<>();
72 
73         observers.put(R.string.pref_enable_client_audio, b -> {
74             synchronized (mLock) {
75                 updateVdmUids(ImmutableSet.copyOf(mRunningVdmUids));
76             }
77         });
78 
79         return observers;
80     }
81 
82     @Override
onTopActivityChanged(int displayId, @NonNull ComponentName componentName)83     public void onTopActivityChanged(int displayId, @NonNull ComponentName componentName) {
84 
85         Optional<Integer> topActivityUid = getUidForComponent(componentName);
86         if (topActivityUid.isEmpty()) {
87             Log.w(TAG, "Cannot determine UID for top activity component " + componentName);
88             return;
89         }
90 
91         final ImmutableSet<Integer> updatedUids;
92         synchronized (mLock) {
93             HashSet<Integer> displayUidSet =
94                     mDisplayIdToRunningUids.computeIfAbsent(displayId, k -> new HashSet<>());
95             displayUidSet.add(topActivityUid.get());
96             mRunningVdmUids =
97                     mDisplayIdToRunningUids.values().stream()
98                             .flatMap(Collection::stream)
99                             .collect(Collectors.toSet());
100             updatedUids = ImmutableSet.copyOf(mRunningVdmUids);
101         }
102 
103         updateVdmUids(updatedUids);
104     }
105 
106     @Override
onDisplayEmpty(int displayId)107     public void onDisplayEmpty(int displayId) {
108         Set<Integer> uidsBefore;
109         ImmutableSet<Integer> uidsAfter;
110         synchronized (mLock) {
111             uidsBefore = mRunningVdmUids;
112             mDisplayIdToRunningUids.remove(displayId);
113             mRunningVdmUids =
114                     mDisplayIdToRunningUids.values().stream()
115                             .flatMap(Collection::stream)
116                             .collect(Collectors.toSet());
117             uidsAfter = ImmutableSet.copyOf(mRunningVdmUids);
118         }
119 
120         if (!uidsAfter.equals(uidsBefore)) {
121             updateVdmUids(uidsAfter);
122         }
123     }
124 
updateVdmUids(ImmutableSet<Integer> uids)125     private void updateVdmUids(ImmutableSet<Integer> uids) {
126         if (mPreferenceController.getBoolean(R.string.pref_enable_client_audio)) {
127             mAudioStreamer.updateVdmUids(uids);
128             mAudioInjector.updateVdmUids(uids);
129         }
130     }
131 
getUidForComponent(@onNull ComponentName topActivity)132     private Optional<Integer> getUidForComponent(@NonNull ComponentName topActivity) {
133         try {
134             return Optional.of(
135                     mPackageManager.getPackageUid(topActivity.getPackageName(), /* flags= */ 0));
136         } catch (NameNotFoundException e) {
137             return Optional.empty();
138         }
139     }
140 }
141