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.server.wifi;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.net.wifi.WifiManager;
22 import android.text.TextUtils;
23 import android.util.ArrayMap;
24 import android.util.Log;
25 
26 import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil;
27 import com.android.server.wifi.util.XmlUtil;
28 
29 import org.xmlpull.v1.XmlPullParser;
30 import org.xmlpull.v1.XmlPullParserException;
31 import org.xmlpull.v1.XmlSerializer;
32 
33 import java.io.FileDescriptor;
34 import java.io.IOException;
35 import java.io.PrintWriter;
36 import java.util.HashMap;
37 import java.util.Map;
38 
39 /**
40  * Store data for storing B2B wifi roaming policies.
41  * These are key (string) / value pairs that are stored in
42  * WifiConfigStore.xml file in a separate section.
43  */
44 public class WifiRoamingConfigStore {
45     private static final String TAG = "WifiRoamingConfigStore";
46     private static final int INVALID_ROAMING_MODE = -1;
47 
48     // To store roaming policies that are added by the device owner (DO) or
49     // the profile owner of an organization owned device (COPE).
50     private final Map<String, Integer> mDeviceAdminRoamingPolicies = new ArrayMap<>();
51     // To store roaming policies that are added by non-admins.
52     private final Map<String, Integer> mNonAdminRoamingPolicies = new ArrayMap<>();
53     private final WifiConfigManager mWifiConfigManager;
54     private boolean mHasNewDataToSerialize = false;
55 
WifiRoamingConfigStore(@onNull WifiConfigManager wifiConfigManager, @NonNull WifiConfigStore wifiConfigStore)56     public WifiRoamingConfigStore(@NonNull WifiConfigManager wifiConfigManager,
57                                   @NonNull WifiConfigStore wifiConfigStore) {
58         mWifiConfigManager = wifiConfigManager;
59         // Register our data store.
60         wifiConfigStore.registerStoreData(new StoreData());
61     }
62 
63     /**
64      * Trigger config store writes in the main wifi service looper's handler.
65      */
triggerSaveToStore()66     private void triggerSaveToStore() {
67         mHasNewDataToSerialize = true;
68         mWifiConfigManager.saveToStore();
69     }
70 
71     /**
72      * Add a roaming policy to the corresponding stored policies.
73      *
74      * @param ssid of the network on which policy to be added.
75      * @param roamingMode denotes roaming mode value configured.
76      * @param isDeviceOwner flag denoting whether API is called by the device owner.
77      */
addRoamingMode(@onNull String ssid, @NonNull int roamingMode, boolean isDeviceOwner)78     public void addRoamingMode(@NonNull String ssid, @NonNull int roamingMode,
79                                boolean isDeviceOwner) {
80         if (isDeviceOwner) {
81             mDeviceAdminRoamingPolicies.put(ssid, roamingMode);
82         } else {
83             mNonAdminRoamingPolicies.put(ssid, roamingMode);
84         }
85         triggerSaveToStore();
86     }
87 
88     /**
89      * Remove a roaming policy from the corresponding stored policies.
90      *
91      * @param ssid of the network on which policy to be removed.
92      * @param isDeviceOwner flag denoting whether API is called by the device owner.
93      */
removeRoamingMode(@onNull String ssid, boolean isDeviceOwner)94     public void removeRoamingMode(@NonNull String ssid, boolean isDeviceOwner) {
95         if (isDeviceOwner) {
96             mDeviceAdminRoamingPolicies.remove(ssid);
97         } else {
98             mNonAdminRoamingPolicies.remove(ssid);
99         }
100         triggerSaveToStore();
101     }
102 
103     /**
104      * Retrieve roaming policy/mode for the given network name.
105      *
106      * @param ssid of the network which needs to be queried to fetch policy.
107      * @return roaming mode stored in policy list,
108      *         {@value WifiManager#ROAMING_MODE_NORMAL} if the key does not exist.
109      */
getRoamingMode(@onNull String ssid)110     public @NonNull int getRoamingMode(@NonNull String ssid) {
111         int roamingMode;
112         roamingMode = mDeviceAdminRoamingPolicies.getOrDefault(ssid, INVALID_ROAMING_MODE);
113         if (roamingMode == INVALID_ROAMING_MODE) {
114             roamingMode = mNonAdminRoamingPolicies.getOrDefault(ssid,
115                     WifiManager.ROAMING_MODE_NORMAL);
116         }
117         return roamingMode;
118     }
119 
120     /**
121      * Get all the network roaming policies configured.
122      *
123      * @param isDeviceOwner flag denoting whether API is called by the device owner.
124      * @return Map of corresponding policies for the API caller,
125      *         where key is ssid and value is roaming mode/policy configured for that ssid.
126      */
getPerSsidRoamingModes(boolean isDeviceOwner)127     public Map<String, Integer> getPerSsidRoamingModes(boolean isDeviceOwner) {
128         Map<String, Integer> roamingPolicies = new ArrayMap<>();
129         if (isDeviceOwner) {
130             roamingPolicies.putAll(mDeviceAdminRoamingPolicies);
131         } else {
132             roamingPolicies.putAll(mNonAdminRoamingPolicies);
133         }
134         return roamingPolicies;
135     }
136 
137     /**
138      * Dump all roaming policies for debugging.
139      */
dump(FileDescriptor fd, PrintWriter pw, String[] args)140     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
141         pw.println();
142         pw.println("Dump of " + TAG);
143         pw.println("DEVICE_ADMIN_POLICIES");
144         for (Map.Entry<String, Integer> entry : mDeviceAdminRoamingPolicies.entrySet()) {
145             pw.print(entry.getKey());
146             pw.print("=");
147             pw.println(entry.getValue());
148         }
149         pw.println();
150         pw.println("NON_ADMIN_POLICIES");
151         for (Map.Entry<String, Integer> entry : mNonAdminRoamingPolicies.entrySet()) {
152             pw.print(entry.getKey());
153             pw.print("=");
154             pw.println(entry.getValue());
155         }
156     }
157 
158     /**
159      * Store data for persisting the roaming policies data to config store.
160      */
161     private class StoreData implements WifiConfigStore.StoreData {
162         private static final String XML_TAG_SECTION_HEADER = "RoamingPolicies";
163         private static final String XML_TAG_DEVICE_ADMIN_POLICIES = "DeviceAdminPolicies";
164         private static final String XML_TAG_NON_ADMIN_POLICIES = "NonAdminPolicies";
165 
166         @Override
serializeData(XmlSerializer out, @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)167         public void serializeData(XmlSerializer out,
168                                   @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
169                 throws XmlPullParserException, IOException {
170             XmlUtil.writeNextValue(out, XML_TAG_DEVICE_ADMIN_POLICIES, mDeviceAdminRoamingPolicies);
171             XmlUtil.writeNextValue(out, XML_TAG_NON_ADMIN_POLICIES, mNonAdminRoamingPolicies);
172             mHasNewDataToSerialize = false;
173         }
174 
175         @Override
deserializeData(XmlPullParser in, int outerTagDepth, @WifiConfigStore.Version int version, @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)176         public void deserializeData(XmlPullParser in, int outerTagDepth,
177                                     @WifiConfigStore.Version int version,
178                                     @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
179                 throws XmlPullParserException, IOException {
180             if (in == null) {
181                 mDeviceAdminRoamingPolicies.clear();
182                 mNonAdminRoamingPolicies.clear();
183                 return;
184             }
185             Map<String, Integer> deviceAdminPolicies = null, nonAdminPolicies = null;
186             while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
187                 String[] valueName = new String[1];
188                 Object value = XmlUtil.readCurrentValue(in, valueName);
189                 if (TextUtils.isEmpty(valueName[0])) {
190                     throw new XmlPullParserException("Missing value name");
191                 }
192                 switch (valueName[0]) {
193                     case XML_TAG_DEVICE_ADMIN_POLICIES:
194                         deviceAdminPolicies = (HashMap) value;
195                         break;
196                     case XML_TAG_NON_ADMIN_POLICIES:
197                         nonAdminPolicies = (HashMap) value;
198                         break;
199                     default:
200                         Log.w(TAG, "Ignoring unknown tag under " + XML_TAG_SECTION_HEADER + ": "
201                                 + valueName[0]);
202                         break;
203                 }
204             }
205             if (deviceAdminPolicies != null) {
206                 mDeviceAdminRoamingPolicies.putAll(deviceAdminPolicies);
207             }
208             if (nonAdminPolicies != null) {
209                 mNonAdminRoamingPolicies.putAll(nonAdminPolicies);
210             }
211         }
212 
213         @Override
resetData()214         public void resetData() {
215             mDeviceAdminRoamingPolicies.clear();
216             mNonAdminRoamingPolicies.clear();
217         }
218 
219         @Override
hasNewDataToSerialize()220         public boolean hasNewDataToSerialize() {
221             return mHasNewDataToSerialize;
222         }
223 
224         @Override
getName()225         public String getName() {
226             return XML_TAG_SECTION_HEADER;
227         }
228 
229         @Override
getStoreFileId()230         public @WifiConfigStore.StoreFileId int getStoreFileId() {
231             // Shared general store.
232             return WifiConfigStore.STORE_FILE_SHARED_GENERAL;
233         }
234     }
235 }
236