1 /*
2  * Copyright (C) 2010 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.services.telephony.sip;
18 
19 import com.android.internal.os.AtomicFile;
20 
21 import android.content.Context;
22 import android.net.sip.SipProfile;
23 import android.text.TextUtils;
24 import android.util.Log;
25 
26 import java.io.File;
27 import java.io.FileOutputStream;
28 import java.io.IOException;
29 import java.io.ObjectInputStream;
30 import java.io.ObjectOutputStream;
31 import java.util.ArrayList;
32 import java.util.Collections;
33 import java.util.List;
34 
35 /**
36  * Utility class that helps perform operations on the SipProfile database.
37  */
38 class SipProfileDb {
39     private static final String PREFIX = "[SipProfileDb] ";
40     private static final boolean VERBOSE = false; /* STOP SHIP if true */
41 
42     private static final String PROFILES_DIR = "/profiles/";
43     private static final String PROFILE_OBJ_FILE = ".pobj";
44 
45     private static final String SCHEME_PREFIX = "sip:";
46 
47     private Context mContext;
48     private String mProfilesDirectory;
49     private SipPreferences mSipPreferences;
50     private int mProfilesCount = -1;
51 
SipProfileDb(Context context)52     public SipProfileDb(Context context) {
53         // Sip Profile Db should always reference CE storage.
54         mContext = context.createCredentialProtectedStorageContext();
55         setupDatabase();
56     }
57 
58     // Only should be used during migration from M->N to move database
accessDEStorageForMigration()59     public void accessDEStorageForMigration() {
60         mContext = mContext.createDeviceProtectedStorageContext();
61         setupDatabase();
62     }
63 
setupDatabase()64     private void setupDatabase() {
65         mProfilesDirectory = mContext.getFilesDir().getAbsolutePath() + PROFILES_DIR;
66         mSipPreferences = new SipPreferences(mContext);
67     }
68 
deleteProfile(SipProfile p)69     public void deleteProfile(SipProfile p) {
70         synchronized(SipProfileDb.class) {
71             deleteProfile(new File(mProfilesDirectory + p.getProfileName()));
72             if (mProfilesCount < 0) retrieveSipProfileListInternal();
73         }
74     }
75 
deleteProfile(File file)76     private void deleteProfile(File file) {
77         if (file.isDirectory()) {
78             for (File child : file.listFiles()) deleteProfile(child);
79         }
80         file.delete();
81     }
82 
cleanupUponMigration()83     public void cleanupUponMigration() {
84         // Remove empty .../profiles/ directory
85         File dbDir = new File(mProfilesDirectory);
86         if(dbDir.isDirectory()) {
87             dbDir.delete();
88         }
89         // Remove SharedPreferences file as well
90         mSipPreferences.clearSharedPreferences();
91     }
92 
saveProfile(SipProfile p)93     public void saveProfile(SipProfile p) throws IOException {
94         synchronized(SipProfileDb.class) {
95             if (mProfilesCount < 0) retrieveSipProfileListInternal();
96             File f = new File(mProfilesDirectory + p.getProfileName());
97             if (!f.exists()) f.mkdirs();
98             AtomicFile atomicFile = new AtomicFile(new File(f, PROFILE_OBJ_FILE));
99             FileOutputStream fos = null;
100             ObjectOutputStream oos = null;
101             try {
102                 fos = atomicFile.startWrite();
103                 oos = new ObjectOutputStream(fos);
104                 oos.writeObject(p);
105                 oos.flush();
106                 atomicFile.finishWrite(fos);
107             } catch (IOException e) {
108                 atomicFile.failWrite(fos);
109                 throw e;
110             } finally {
111                 if (oos != null) oos.close();
112             }
113         }
114     }
115 
retrieveSipProfileList()116     public List<SipProfile> retrieveSipProfileList() {
117         synchronized(SipProfileDb.class) {
118             return retrieveSipProfileListInternal();
119         }
120     }
121 
retrieveSipProfileListInternal()122     private List<SipProfile> retrieveSipProfileListInternal() {
123         List<SipProfile> sipProfileList = Collections.synchronizedList(
124                 new ArrayList<SipProfile>());
125 
126         File root = new File(mProfilesDirectory);
127         String[] dirs = root.list();
128         if (dirs == null) return sipProfileList;
129         for (String dir : dirs) {
130             SipProfile p = retrieveSipProfileFromName(dir);
131             if (p == null) continue;
132             sipProfileList.add(p);
133         }
134         mProfilesCount = sipProfileList.size();
135         return sipProfileList;
136     }
137 
retrieveSipProfileFromName(String name)138     public SipProfile retrieveSipProfileFromName(String name) {
139         if (TextUtils.isEmpty(name)) {
140             return null;
141         }
142 
143         File root = new File(mProfilesDirectory);
144         File f = new File(new File(root, name), PROFILE_OBJ_FILE);
145         if (f.exists()) {
146             try {
147                 SipProfile p = deserialize(f);
148                 if (p != null && name.equals(p.getProfileName())) {
149                     return p;
150                 }
151             } catch (IOException e) {
152                 log("retrieveSipProfileListInternal, exception: " + e);
153             }
154         }
155         return null;
156     }
157 
deserialize(File profileObjectFile)158     private SipProfile deserialize(File profileObjectFile) throws IOException {
159         AtomicFile atomicFile = new AtomicFile(profileObjectFile);
160         ObjectInputStream ois = null;
161         try {
162             ois = new ObjectInputStream(atomicFile.openRead());
163             SipProfile p = (SipProfile) ois.readObject();
164             return p;
165         } catch (ClassNotFoundException e) {
166             log("deserialize, exception: " + e);
167         } finally {
168             if (ois!= null) ois.close();
169         }
170         return null;
171     }
172 
log(String msg)173     private static void log(String msg) {
174         Log.d(SipUtil.LOG_TAG, PREFIX + msg);
175     }
176 }
177