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 android.content.Context; 20 import android.net.sip.SipProfile; 21 import android.text.TextUtils; 22 import android.util.AtomicFile; 23 import android.util.EventLog; 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) throws IOException { 70 synchronized(SipProfileDb.class) { 71 File profileFile = new File(mProfilesDirectory, p.getProfileName()); 72 if (!isChild(new File(mProfilesDirectory), profileFile)) { 73 throw new IOException("Invalid Profile Credentials!"); 74 } 75 deleteProfile(profileFile); 76 if (mProfilesCount < 0) retrieveSipProfileListInternal(); 77 } 78 } 79 deleteProfile(File file)80 private void deleteProfile(File file) { 81 if (file.isDirectory()) { 82 for (File child : file.listFiles()) deleteProfile(child); 83 } 84 file.delete(); 85 } 86 cleanupUponMigration()87 public void cleanupUponMigration() { 88 // Remove empty .../profiles/ directory 89 File dbDir = new File(mProfilesDirectory); 90 if(dbDir.isDirectory()) { 91 dbDir.delete(); 92 } 93 // Remove SharedPreferences file as well 94 mSipPreferences.clearSharedPreferences(); 95 } 96 saveProfile(SipProfile p)97 public void saveProfile(SipProfile p) throws IOException { 98 synchronized(SipProfileDb.class) { 99 if (mProfilesCount < 0) retrieveSipProfileListInternal(); 100 File f = new File(mProfilesDirectory, p.getProfileName()); 101 if (!isChild(new File(mProfilesDirectory), f)) { 102 throw new IOException("Invalid Profile Credentials!"); 103 } 104 if (!f.exists()) f.mkdirs(); 105 AtomicFile atomicFile = new AtomicFile(new File(f, PROFILE_OBJ_FILE)); 106 FileOutputStream fos = null; 107 ObjectOutputStream oos = null; 108 try { 109 fos = atomicFile.startWrite(); 110 oos = new ObjectOutputStream(fos); 111 oos.writeObject(p); 112 oos.flush(); 113 atomicFile.finishWrite(fos); 114 } catch (IOException e) { 115 atomicFile.failWrite(fos); 116 throw e; 117 } finally { 118 if (oos != null) oos.close(); 119 } 120 } 121 } 122 retrieveSipProfileList()123 public List<SipProfile> retrieveSipProfileList() { 124 synchronized(SipProfileDb.class) { 125 return retrieveSipProfileListInternal(); 126 } 127 } 128 retrieveSipProfileListInternal()129 private List<SipProfile> retrieveSipProfileListInternal() { 130 List<SipProfile> sipProfileList = Collections.synchronizedList( 131 new ArrayList<SipProfile>()); 132 133 File root = new File(mProfilesDirectory); 134 String[] dirs = root.list(); 135 if (dirs == null) return sipProfileList; 136 for (String dir : dirs) { 137 SipProfile p = retrieveSipProfileFromName(dir); 138 if (p == null) continue; 139 sipProfileList.add(p); 140 } 141 mProfilesCount = sipProfileList.size(); 142 return sipProfileList; 143 } 144 retrieveSipProfileFromName(String name)145 public SipProfile retrieveSipProfileFromName(String name) { 146 if (TextUtils.isEmpty(name)) { 147 return null; 148 } 149 150 File root = new File(mProfilesDirectory); 151 File f = new File(new File(root, name), PROFILE_OBJ_FILE); 152 if (f.exists()) { 153 try { 154 SipProfile p = deserialize(f); 155 if (p != null && name.equals(p.getProfileName())) { 156 return p; 157 } 158 } catch (IOException e) { 159 log("retrieveSipProfileListInternal, exception: " + e); 160 } 161 } 162 return null; 163 } 164 deserialize(File profileObjectFile)165 private SipProfile deserialize(File profileObjectFile) throws IOException { 166 AtomicFile atomicFile = new AtomicFile(profileObjectFile); 167 ObjectInputStream ois = null; 168 try { 169 ois = new ObjectInputStream(atomicFile.openRead()); 170 SipProfile p = (SipProfile) ois.readObject(); 171 return p; 172 } catch (ClassNotFoundException e) { 173 log("deserialize, exception: " + e); 174 } finally { 175 if (ois!= null) ois.close(); 176 } 177 return null; 178 } 179 log(String msg)180 private static void log(String msg) { 181 Log.d(SipUtil.LOG_TAG, PREFIX + msg); 182 } 183 184 /** 185 * Verifies that the file is a direct child of the base directory. 186 */ isChild(File base, File file)187 private boolean isChild(File base, File file) { 188 if (base == null || file == null) { 189 return false; 190 } 191 if (!base.equals(file.getAbsoluteFile().getParentFile())) { 192 Log.w(SipUtil.LOG_TAG, "isChild, file is not a child of the base dir."); 193 EventLog.writeEvent(0x534e4554, "31530456", -1, ""); 194 return false; 195 } 196 return true; 197 } 198 } 199