1 package com.android.server.deviceconfig;
2 
3 import static com.android.server.deviceconfig.Flags.enableRebootNotification;
4 import static com.android.server.deviceconfig.Flags.enableUnattendedReboot;
5 
6 import java.io.IOException;
7 import java.io.FileDescriptor;
8 import java.io.FileInputStream;
9 import java.io.IOException;
10 import java.util.HashSet;
11 import java.util.HashMap;
12 import java.util.Map;
13 import java.util.Set;
14 
15 import android.aconfig.Aconfig.parsed_flags;
16 import android.aconfig.Aconfig.parsed_flag;
17 import android.content.Intent;
18 import android.annotation.NonNull;
19 import android.annotation.SystemApi;
20 import android.content.BroadcastReceiver;
21 import android.content.Context;
22 import android.os.AsyncTask;
23 import android.os.Binder;
24 import android.content.IntentFilter;
25 import android.provider.DeviceConfig;
26 import android.provider.UpdatableDeviceConfigServiceReadiness;
27 import android.content.ServiceConnection;
28 import android.os.IBinder;
29 import android.content.ComponentName;
30 import android.util.Slog;
31 import com.android.modules.utils.build.SdkLevel;
32 import com.android.server.SystemService;
33 
34 /** @hide */
35 @SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
36 public class DeviceConfigInit {
37     private static final String TAG = "DEVICE_CONFIG_INIT";
38     private static final String STAGED_NAMESPACE = "staged";
39 
40     private static final String SYSTEM_FLAGS_PATH = "/system/etc/aconfig_flags.pb";
41     private static final String SYSTEM_EXT_FLAGS_PATH = "/system_ext/etc/aconfig_flags.pb";
42     private static final String VENDOR_FLAGS_PATH = "/vendor/etc/aconfig_flags.pb";
43 
44     private static final String CONFIGURATION_NAMESPACE = "configuration";
45     private static final String BOOT_NOTIFICATION_FLAG =
46             "ConfigInfraFlags__enable_boot_notification";
47     private static final String UNATTENDED_REBOOT_FLAG =
48             "ConfigInfraFlags__enable_unattended_reboot";
49 
DeviceConfigInit()50     private DeviceConfigInit() {
51         // do not instantiate
52     }
53 
54     /** @hide */
55     @SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
56     public static class Lifecycle extends SystemService {
57         private DeviceConfigServiceImpl mService;
58         private UnattendedRebootManager mUnattendedRebootManager;
59 
60         /** @hide */
61         @SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
Lifecycle(@onNull Context context)62         public Lifecycle(@NonNull Context context) {
63             super(context);
64             // this service is always instantiated but should only launch subsequent service(s)
65             // if the module is ready
66             if (UpdatableDeviceConfigServiceReadiness.shouldStartUpdatableService()) {
67                 mService = new DeviceConfigServiceImpl(getContext());
68                 publishBinderService(DeviceConfig.SERVICE_NAME, mService);
69             }
70             applyBootstrapValues();
71         }
72 
73         /** @hide */
74         @Override
onStart()75         public void onStart() {
76             boolean notificationEnabled =
77                     DeviceConfig.getBoolean(CONFIGURATION_NAMESPACE, BOOT_NOTIFICATION_FLAG, false);
78             if (notificationEnabled && enableRebootNotification()) {
79                 Map<String, Set<String>> aconfigFlags = new HashMap<>();
80                 try {
81                     addAconfigFlagsFromFile(aconfigFlags, SYSTEM_FLAGS_PATH);
82                     addAconfigFlagsFromFile(aconfigFlags, SYSTEM_EXT_FLAGS_PATH);
83                     addAconfigFlagsFromFile(aconfigFlags, VENDOR_FLAGS_PATH);
84                 } catch (IOException e) {
85                     Slog.e(TAG, "error loading aconfig flags", e);
86                 }
87 
88                 BootNotificationCreator notifCreator =
89                         new BootNotificationCreator(
90                                 getContext().getApplicationContext(), aconfigFlags);
91 
92                 DeviceConfig.addOnPropertiesChangedListener(
93                         STAGED_NAMESPACE, AsyncTask.THREAD_POOL_EXECUTOR, notifCreator);
94             }
95 
96             boolean unattendedRebootEnabled =
97                     DeviceConfig.getBoolean(CONFIGURATION_NAMESPACE, UNATTENDED_REBOOT_FLAG, false);
98             if (unattendedRebootEnabled && enableUnattendedReboot()) {
99                 // Only schedule a reboot if this is the first boot since an OTA.
100                 if (!getContext().getPackageManager().isDeviceUpgrading()) {
101                     return;
102                 }
103 
104                 mUnattendedRebootManager =
105                         new UnattendedRebootManager(getContext().getApplicationContext());
106                 Slog.i(TAG, "Registering receivers");
107                 getContext()
108                         .registerReceiver(
109                                 new BroadcastReceiver() {
110                                     @Override
111                                     public void onReceive(Context context, Intent intent) {
112                                         mUnattendedRebootManager.prepareUnattendedReboot();
113                                         mUnattendedRebootManager.scheduleReboot();
114                                     }
115                                 },
116                                 new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
117                 getContext()
118                         .registerReceiver(
119                                 new BroadcastReceiver() {
120                                     @Override
121                                     public void onReceive(Context context, Intent intent) {
122                                         mUnattendedRebootManager.maybePrepareUnattendedReboot();
123                                     }
124                                 },
125                                 new IntentFilter(Intent.ACTION_LOCKED_BOOT_COMPLETED));
126             }
127         }
128 
addAconfigFlagsFromFile(Map<String, Set<String>> aconfigFlags, String fileName)129         private void addAconfigFlagsFromFile(Map<String, Set<String>> aconfigFlags, String fileName)
130                 throws IOException {
131             byte[] contents = (new FileInputStream(fileName)).readAllBytes();
132             parsed_flags parsedFlags = parsed_flags.parseFrom(contents);
133             for (parsed_flag flag : parsedFlags.getParsedFlagList()) {
134                 if (aconfigFlags.get(flag.getNamespace()) == null) {
135                     aconfigFlags.put(flag.getNamespace(), new HashSet<>());
136                     aconfigFlags.get(flag.getNamespace()).add(flag.getName());
137                 } else {
138                     aconfigFlags.get(flag.getNamespace()).add(flag.getName());
139                 }
140             }
141         }
142 
applyBootstrapValues()143         private void applyBootstrapValues() {
144             if (SdkLevel.isAtLeastV()) {
145                 try {
146                     new DeviceConfigBootstrapValues().applyValuesIfNeeded();
147                 } catch (RuntimeException e) {
148                     Slog.e(TAG, "Failed to load boot overrides", e);
149                     throw e;
150                 } catch (IOException e) {
151                     Slog.e(TAG, "Failed to load boot overrides", e);
152                     throw new RuntimeException(e);
153                 }
154             }
155         }
156     }
157 }
158