1 /*
2  * Copyright (C) 2024 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.android.system.virtualmachine;
18 
19 import android.app.job.JobScheduler;
20 import android.content.BroadcastReceiver;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.os.Handler;
25 import android.os.IBinder;
26 import android.os.ServiceManager;
27 import android.os.UserHandle;
28 import android.system.virtualizationmaintenance.IVirtualizationMaintenance;
29 import android.util.Log;
30 
31 import com.android.internal.os.BackgroundThread;
32 import com.android.server.SystemService;
33 
34 /**
35  * This class exists to notify virtualization service of relevant things happening in the Android
36  * framework.
37  *
38  * <p>It currently is responsible for Secretkeeper-related maintenance - ensuring that we are not
39  * storing secrets for apps or users that no longer exist.
40  */
41 public class VirtualizationSystemService extends SystemService {
42     private static final String TAG = VirtualizationSystemService.class.getName();
43     private static final String SERVICE_NAME = "android.system.virtualizationmaintenance";
44     private Handler mHandler;
45 
VirtualizationSystemService(Context context)46     public VirtualizationSystemService(Context context) {
47         super(context);
48     }
49 
50     @Override
onStart()51     public void onStart() {
52         // Nothing needed here - we don't expose any binder service. The binder service we use is
53         // exposed as a lazy service by the virtualizationservice native binary.
54     }
55 
56     @Override
onBootPhase(int phase)57     public void onBootPhase(int phase) {
58         if (phase != PHASE_BOOT_COMPLETED) return;
59 
60         mHandler = BackgroundThread.getHandler();
61         new Receiver().registerForBroadcasts();
62 
63         SecretkeeperJobService.scheduleJob(getContext().getSystemService(JobScheduler.class));
64     }
65 
notifyAppRemoved(int uid)66     private void notifyAppRemoved(int uid) {
67         try {
68             IVirtualizationMaintenance maintenance = connectToMaintenanceService();
69             maintenance.appRemoved(UserHandle.getUserId(uid), UserHandle.getAppId(uid));
70         } catch (Exception e) {
71             Log.e(TAG, "notifyAppRemoved failed", e);
72         }
73     }
74 
notifyUserRemoved(int userId)75     private void notifyUserRemoved(int userId) {
76         try {
77             IVirtualizationMaintenance maintenance = connectToMaintenanceService();
78             maintenance.userRemoved(userId);
79         } catch (Exception e) {
80             Log.e(TAG, "notifyUserRemoved failed", e);
81         }
82     }
83 
connectToMaintenanceService()84     static IVirtualizationMaintenance connectToMaintenanceService() {
85         IBinder binder = ServiceManager.waitForService(SERVICE_NAME);
86         IVirtualizationMaintenance maintenance =
87                 IVirtualizationMaintenance.Stub.asInterface(binder);
88         if (maintenance == null) {
89             throw new IllegalStateException("Failed to connect to " + SERVICE_NAME);
90         }
91         return maintenance;
92     }
93 
94     private class Receiver extends BroadcastReceiver {
registerForBroadcasts()95         public void registerForBroadcasts() {
96             Context allUsers = getContext().createContextAsUser(UserHandle.ALL, 0 /* flags */);
97 
98             allUsers.registerReceiver(this, new IntentFilter(Intent.ACTION_USER_REMOVED));
99 
100             IntentFilter packageFilter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
101             packageFilter.addDataScheme("package");
102             allUsers.registerReceiver(this, packageFilter);
103         }
104 
105         @Override
onReceive(Context context, Intent intent)106         public void onReceive(Context context, Intent intent) {
107             switch (intent.getAction()) {
108                 case Intent.ACTION_USER_REMOVED:
109                     onUserRemoved(intent);
110                     break;
111                 case Intent.ACTION_PACKAGE_REMOVED:
112                     onPackageRemoved(intent);
113                     break;
114                 default:
115                     Log.e(TAG, "received unexpected intent: " + intent.getAction());
116                     break;
117             }
118         }
119 
onUserRemoved(Intent intent)120         private void onUserRemoved(Intent intent) {
121             int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
122             if (userId != UserHandle.USER_NULL) {
123                 mHandler.post(() -> notifyUserRemoved(userId));
124             }
125         }
126 
onPackageRemoved(Intent intent)127         private void onPackageRemoved(Intent intent) {
128             if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)
129                     || !intent.getBooleanExtra(Intent.EXTRA_DATA_REMOVED, false)) {
130                 // Package is being updated rather than uninstalled.
131                 return;
132             }
133             int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
134             if (uid != -1) {
135                 mHandler.post(() -> notifyAppRemoved(uid));
136             }
137         }
138     }
139 }
140