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