1 /*
2  * Copyright (C) 2014 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.ethernet;
18 
19 import static com.android.net.module.util.DeviceConfigUtils.TETHERING_MODULE_NAME;
20 
21 import android.content.ApexEnvironment;
22 import android.net.IpConfiguration;
23 import android.os.Environment;
24 import android.util.ArrayMap;
25 import android.util.AtomicFile;
26 import android.util.Log;
27 
28 import com.android.internal.annotations.VisibleForTesting;
29 import com.android.server.net.IpConfigStore;
30 
31 import java.io.File;
32 import java.io.FileOutputStream;
33 import java.io.IOException;
34 
35 /**
36  * This class provides an API to store and manage Ethernet network configuration.
37  */
38 public class EthernetConfigStore {
39     private static final String TAG = EthernetConfigStore.class.getSimpleName();
40     private static final String CONFIG_FILE = "ipconfig.txt";
41     private static final String FILE_PATH = "/misc/ethernet/";
42     private static final String LEGACY_IP_CONFIG_FILE_PATH = Environment.getDataDirectory()
43             + FILE_PATH;
44     private static final String APEX_IP_CONFIG_FILE_PATH = ApexEnvironment.getApexEnvironment(
45             TETHERING_MODULE_NAME).getDeviceProtectedDataDir() + FILE_PATH;
46 
47     private IpConfigStore mStore = new IpConfigStore();
48     private final ArrayMap<String, IpConfiguration> mIpConfigurations;
49     private final Object mSync = new Object();
50 
EthernetConfigStore()51     public EthernetConfigStore() {
52         mIpConfigurations = new ArrayMap<>(0);
53     }
54 
doesConfigFileExist(final String filepath)55     private static boolean doesConfigFileExist(final String filepath) {
56         return new File(filepath).exists();
57     }
58 
writeLegacyIpConfigToApexPath(final String newFilePath, final String oldFilePath, final String filename)59     private void writeLegacyIpConfigToApexPath(final String newFilePath, final String oldFilePath,
60             final String filename) {
61         final File directory = new File(newFilePath);
62         if (!directory.exists()) {
63             directory.mkdirs();
64         }
65 
66         // Write the legacy IP config to the apex file path.
67         FileOutputStream fos = null;
68         final AtomicFile dst = new AtomicFile(new File(newFilePath + filename));
69         final AtomicFile src = new AtomicFile(new File(oldFilePath + filename));
70         try {
71             final byte[] raw = src.readFully();
72             if (raw.length > 0) {
73                 fos = dst.startWrite();
74                 fos.write(raw);
75                 fos.flush();
76                 dst.finishWrite(fos);
77             }
78         } catch (IOException e) {
79             Log.e(TAG, "Fail to sync the legacy IP config to the apex file path.");
80             dst.failWrite(fos);
81         }
82     }
83 
read()84     public void read() {
85         read(APEX_IP_CONFIG_FILE_PATH, LEGACY_IP_CONFIG_FILE_PATH, CONFIG_FILE);
86     }
87 
88     @VisibleForTesting
read(final String newFilePath, final String oldFilePath, final String filename)89     void read(final String newFilePath, final String oldFilePath, final String filename) {
90         synchronized (mSync) {
91             // Attempt to read the IP configuration from apex file path first.
92             if (doesConfigFileExist(newFilePath + filename)) {
93                 loadConfigFileLocked(newFilePath + filename);
94                 return;
95             }
96 
97             // If the config file doesn't exist in the apex file path, attempt to read it from
98             // the legacy file path, if config file exists, write the legacy IP configuration to
99             // apex config file path, this should just happen on the first boot. New or updated
100             // config entries are only written to the apex config file later.
101             if (!doesConfigFileExist(oldFilePath + filename)) return;
102             loadConfigFileLocked(oldFilePath + filename);
103             writeLegacyIpConfigToApexPath(newFilePath, oldFilePath, filename);
104         }
105     }
106 
loadConfigFileLocked(final String filepath)107     private void loadConfigFileLocked(final String filepath) {
108         // readIpConfigurations can return null when the version is invalid.
109         final ArrayMap<String, IpConfiguration> configs =
110                 IpConfigStore.readIpConfigurations(filepath);
111         if (configs == null) {
112             Log.e(TAG, "IpConfigStore#readIpConfigurations() returned null");
113             return;
114         }
115         mIpConfigurations.putAll(configs);
116     }
117 
write(String iface, IpConfiguration config)118     public void write(String iface, IpConfiguration config) {
119         final File directory = new File(APEX_IP_CONFIG_FILE_PATH);
120         if (!directory.exists()) {
121             directory.mkdirs();
122         }
123         write(iface, config, APEX_IP_CONFIG_FILE_PATH + CONFIG_FILE);
124     }
125 
126     @VisibleForTesting
write(String iface, IpConfiguration config, String filepath)127     void write(String iface, IpConfiguration config, String filepath) {
128         boolean modified;
129 
130         synchronized (mSync) {
131             if (config == null) {
132                 modified = mIpConfigurations.remove(iface) != null;
133             } else {
134                 IpConfiguration oldConfig = mIpConfigurations.put(iface, config);
135                 modified = !config.equals(oldConfig);
136             }
137 
138             if (modified) {
139                 mStore.writeIpConfigurations(filepath, mIpConfigurations);
140             }
141         }
142     }
143 
getIpConfigurations()144     public ArrayMap<String, IpConfiguration> getIpConfigurations() {
145         synchronized (mSync) {
146             return new ArrayMap<>(mIpConfigurations);
147         }
148     }
149 }
150