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 package com.android.server.flags;
17 
18 import static android.Manifest.permission.SYNC_FLAGS;
19 import static android.Manifest.permission.WRITE_FLAGS;
20 
21 import android.content.Context;
22 import android.content.pm.PackageManager;
23 import android.flags.FeatureFlags;
24 import android.util.Slog;
25 
26 import com.android.internal.annotations.VisibleForTesting;
27 import com.android.server.SystemService;
28 
29 /**
30  * A service that manages syncing {@link android.flags.FeatureFlags} across processes.
31  *
32  * This service holds flags stable for at least the lifetime of a process, meaning that if
33  * a process comes online with a flag set to true, any other process that connects here and
34  * tries to read the same flag will also receive the flag as true. The flag will remain stable
35  * until either all of the interested processes have died, or the device restarts.
36  *
37  * TODO(279054964): Add to dumpsys
38  * @hide
39  */
40 public class FeatureFlagsService extends SystemService {
41 
42     static final String TAG = "FeatureFlagsService";
43     private final FlagOverrideStore mFlagStore;
44     private final FlagsShellCommand mShellCommand;
45 
46     /**
47      * Initializes the system service.
48      *
49      * @param context The system server context.
50      */
FeatureFlagsService(Context context)51     public FeatureFlagsService(Context context) {
52         super(context);
53         mFlagStore = new FlagOverrideStore(
54                 new GlobalSettingsProxy(context.getContentResolver()));
55         mShellCommand = new FlagsShellCommand(mFlagStore);
56     }
57 
58     @Override
onStart()59     public void onStart() {
60         Slog.d(TAG, "Started Feature Flag Service");
61         FeatureFlagsBinder service = new FeatureFlagsBinder(
62                 mFlagStore, mShellCommand, new PermissionsChecker(getContext()));
63         publishBinderService(
64                 Context.FEATURE_FLAGS_SERVICE, service);
65         publishLocalService(FeatureFlags.class, new FeatureFlags(service));
66     }
67 
68     @Override
onBootPhase(int phase)69     public void onBootPhase(int phase) {
70         super.onBootPhase(phase);
71 
72         if (phase == PHASE_SYSTEM_SERVICES_READY) {
73             // Immediately sync our core flags so that they get locked in. We don't want third-party
74             // apps to override them, and syncing immediately is the easiest way to prevent that.
75             FeatureFlags.getInstance().sync();
76         }
77     }
78 
79     /**
80      * Delegate for checking flag permissions.
81      */
82     @VisibleForTesting
83     public static class PermissionsChecker {
84         private final Context mContext;
85 
86         @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
PermissionsChecker(Context context)87         public PermissionsChecker(Context context) {
88             mContext = context;
89         }
90 
91         /**
92          * Ensures that the caller has {@link SYNC_FLAGS} permission.
93          */
94         @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
assertSyncPermission()95         public void assertSyncPermission() {
96             if (mContext.checkCallingOrSelfPermission(SYNC_FLAGS)
97                     != PackageManager.PERMISSION_GRANTED) {
98                 throw new SecurityException(
99                         "Non-core flag queried. Requires SYNC_FLAGS permission!");
100             }
101         }
102 
103         /**
104          * Ensures that the caller has {@link WRITE_FLAGS} permission.
105          */
106         @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
assertWritePermission()107         public void assertWritePermission() {
108             if (mContext.checkCallingPermission(WRITE_FLAGS) != PackageManager.PERMISSION_GRANTED) {
109                 throw new SecurityException("Requires WRITE_FLAGS permission!");
110             }
111         }
112     }
113 }
114