1 /* 2 * Copyright (C) 2020 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.people.data; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.UserIdInt; 22 import android.annotation.WorkerThread; 23 import android.os.Environment; 24 import android.text.TextUtils; 25 import android.util.ArrayMap; 26 import android.util.Slog; 27 28 import java.io.ByteArrayInputStream; 29 import java.io.ByteArrayOutputStream; 30 import java.io.DataInputStream; 31 import java.io.DataOutputStream; 32 import java.io.File; 33 import java.io.IOException; 34 import java.util.Map; 35 import java.util.concurrent.ScheduledExecutorService; 36 import java.util.function.Consumer; 37 38 /** The data associated with a user profile. */ 39 class UserData { 40 41 private static final String TAG = UserData.class.getSimpleName(); 42 43 private static final int CONVERSATIONS_END_TOKEN = -1; 44 45 private final @UserIdInt int mUserId; 46 47 private final File mPerUserPeopleDataDir; 48 49 private final ScheduledExecutorService mScheduledExecutorService; 50 51 private boolean mIsUnlocked; 52 53 private Map<String, PackageData> mPackageDataMap = new ArrayMap<>(); 54 55 @Nullable 56 private String mDefaultDialer; 57 58 @Nullable 59 private String mDefaultSmsApp; 60 UserData(@serIdInt int userId, @NonNull ScheduledExecutorService scheduledExecutorService)61 UserData(@UserIdInt int userId, @NonNull ScheduledExecutorService scheduledExecutorService) { 62 mUserId = userId; 63 mPerUserPeopleDataDir = new File(Environment.getDataSystemCeDirectory(mUserId), "people"); 64 mScheduledExecutorService = scheduledExecutorService; 65 } 66 getUserId()67 @UserIdInt int getUserId() { 68 return mUserId; 69 } 70 forAllPackages(@onNull Consumer<PackageData> consumer)71 void forAllPackages(@NonNull Consumer<PackageData> consumer) { 72 for (PackageData packageData : mPackageDataMap.values()) { 73 consumer.accept(packageData); 74 } 75 } 76 setUserUnlocked()77 void setUserUnlocked() { 78 mIsUnlocked = true; 79 } 80 setUserStopped()81 void setUserStopped() { 82 mIsUnlocked = false; 83 } 84 isUnlocked()85 boolean isUnlocked() { 86 return mIsUnlocked; 87 } 88 89 @WorkerThread loadUserData()90 void loadUserData() { 91 mPerUserPeopleDataDir.mkdir(); 92 Map<String, PackageData> packageDataMap = PackageData.packagesDataFromDisk( 93 mUserId, this::isDefaultDialer, this::isDefaultSmsApp, mScheduledExecutorService, 94 mPerUserPeopleDataDir); 95 mPackageDataMap.putAll(packageDataMap); 96 } 97 98 /** 99 * Gets the {@link PackageData} for the specified {@code packageName} if exists; otherwise 100 * creates a new instance and returns it. 101 */ 102 @NonNull getOrCreatePackageData(String packageName)103 PackageData getOrCreatePackageData(String packageName) { 104 return mPackageDataMap.computeIfAbsent(packageName, key -> createPackageData(packageName)); 105 } 106 107 /** 108 * Gets the {@link PackageData} for the specified {@code packageName} if exists; otherwise 109 * returns {@code null}. 110 */ 111 @Nullable getPackageData(@onNull String packageName)112 PackageData getPackageData(@NonNull String packageName) { 113 return mPackageDataMap.get(packageName); 114 } 115 116 /** Deletes the specified package data. */ deletePackageData(@onNull String packageName)117 void deletePackageData(@NonNull String packageName) { 118 PackageData packageData = mPackageDataMap.remove(packageName); 119 if (packageData != null) { 120 packageData.onDestroy(); 121 } 122 } 123 setDefaultDialer(@ullable String packageName)124 void setDefaultDialer(@Nullable String packageName) { 125 mDefaultDialer = packageName; 126 } 127 128 @Nullable getDefaultDialer()129 PackageData getDefaultDialer() { 130 return mDefaultDialer != null ? getPackageData(mDefaultDialer) : null; 131 } 132 setDefaultSmsApp(@ullable String packageName)133 void setDefaultSmsApp(@Nullable String packageName) { 134 mDefaultSmsApp = packageName; 135 } 136 137 @Nullable getDefaultSmsApp()138 PackageData getDefaultSmsApp() { 139 return mDefaultSmsApp != null ? getPackageData(mDefaultSmsApp) : null; 140 } 141 142 @Nullable getBackupPayload()143 byte[] getBackupPayload() { 144 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 145 DataOutputStream out = new DataOutputStream(baos); 146 for (PackageData packageData : mPackageDataMap.values()) { 147 try { 148 byte[] conversationsBackupPayload = 149 packageData.getConversationStore().getBackupPayload(); 150 out.writeInt(conversationsBackupPayload.length); 151 out.write(conversationsBackupPayload); 152 out.writeUTF(packageData.getPackageName()); 153 } catch (IOException e) { 154 Slog.e(TAG, "Failed to write conversations to backup payload.", e); 155 return null; 156 } 157 } 158 try { 159 out.writeInt(CONVERSATIONS_END_TOKEN); 160 } catch (IOException e) { 161 Slog.e(TAG, "Failed to write conversations end token to backup payload.", e); 162 return null; 163 } 164 return baos.toByteArray(); 165 } 166 restore(@onNull byte[] payload)167 void restore(@NonNull byte[] payload) { 168 DataInputStream in = new DataInputStream(new ByteArrayInputStream(payload)); 169 try { 170 for (int conversationsPayloadSize = in.readInt(); 171 conversationsPayloadSize != CONVERSATIONS_END_TOKEN; 172 conversationsPayloadSize = in.readInt()) { 173 byte[] conversationsPayload = new byte[conversationsPayloadSize]; 174 in.readFully(conversationsPayload, 0, conversationsPayloadSize); 175 String packageName = in.readUTF(); 176 getOrCreatePackageData(packageName).getConversationStore().restore( 177 conversationsPayload); 178 } 179 } catch (IOException e) { 180 Slog.e(TAG, "Failed to restore conversations from backup payload.", e); 181 } 182 } 183 createPackageData(String packageName)184 private PackageData createPackageData(String packageName) { 185 return new PackageData(packageName, mUserId, this::isDefaultDialer, this::isDefaultSmsApp, 186 mScheduledExecutorService, mPerUserPeopleDataDir); 187 } 188 isDefaultDialer(String packageName)189 private boolean isDefaultDialer(String packageName) { 190 return TextUtils.equals(mDefaultDialer, packageName); 191 } 192 isDefaultSmsApp(String packageName)193 private boolean isDefaultSmsApp(String packageName) { 194 return TextUtils.equals(mDefaultSmsApp, packageName); 195 } 196 } 197