blob: 429d5b7193ceb75bd9c9b0ea855e82fe0a534c0d [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.people.data;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
import android.os.Environment;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Slog;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Consumer;
/** The data associated with a user profile. */
class UserData {
private static final String TAG = UserData.class.getSimpleName();
private static final int CONVERSATIONS_END_TOKEN = -1;
private final @UserIdInt int mUserId;
private final File mPerUserPeopleDataDir;
private final ScheduledExecutorService mScheduledExecutorService;
private boolean mIsUnlocked;
private Map<String, PackageData> mPackageDataMap = new ArrayMap<>();
@Nullable
private String mDefaultDialer;
@Nullable
private String mDefaultSmsApp;
UserData(@UserIdInt int userId, @NonNull ScheduledExecutorService scheduledExecutorService) {
mUserId = userId;
mPerUserPeopleDataDir = new File(Environment.getDataSystemCeDirectory(mUserId), "people");
mScheduledExecutorService = scheduledExecutorService;
}
@UserIdInt int getUserId() {
return mUserId;
}
void forAllPackages(@NonNull Consumer<PackageData> consumer) {
for (PackageData packageData : mPackageDataMap.values()) {
consumer.accept(packageData);
}
}
void setUserUnlocked() {
mIsUnlocked = true;
}
void setUserStopped() {
mIsUnlocked = false;
}
boolean isUnlocked() {
return mIsUnlocked;
}
@WorkerThread
void loadUserData() {
mPerUserPeopleDataDir.mkdir();
Map<String, PackageData> packageDataMap = PackageData.packagesDataFromDisk(
mUserId, this::isDefaultDialer, this::isDefaultSmsApp, mScheduledExecutorService,
mPerUserPeopleDataDir);
mPackageDataMap.putAll(packageDataMap);
}
/**
* Gets the {@link PackageData} for the specified {@code packageName} if exists; otherwise
* creates a new instance and returns it.
*/
@NonNull
PackageData getOrCreatePackageData(String packageName) {
return mPackageDataMap.computeIfAbsent(packageName, key -> createPackageData(packageName));
}
/**
* Gets the {@link PackageData} for the specified {@code packageName} if exists; otherwise
* returns {@code null}.
*/
@Nullable
PackageData getPackageData(@NonNull String packageName) {
return mPackageDataMap.get(packageName);
}
/** Deletes the specified package data. */
void deletePackageData(@NonNull String packageName) {
PackageData packageData = mPackageDataMap.remove(packageName);
if (packageData != null) {
packageData.onDestroy();
}
}
void setDefaultDialer(@Nullable String packageName) {
mDefaultDialer = packageName;
}
@Nullable
PackageData getDefaultDialer() {
return mDefaultDialer != null ? getPackageData(mDefaultDialer) : null;
}
void setDefaultSmsApp(@Nullable String packageName) {
mDefaultSmsApp = packageName;
}
@Nullable
PackageData getDefaultSmsApp() {
return mDefaultSmsApp != null ? getPackageData(mDefaultSmsApp) : null;
}
@Nullable
byte[] getBackupPayload() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(baos);
for (PackageData packageData : mPackageDataMap.values()) {
try {
byte[] conversationsBackupPayload =
packageData.getConversationStore().getBackupPayload();
out.writeInt(conversationsBackupPayload.length);
out.write(conversationsBackupPayload);
out.writeUTF(packageData.getPackageName());
} catch (IOException e) {
Slog.e(TAG, "Failed to write conversations to backup payload.", e);
return null;
}
}
try {
out.writeInt(CONVERSATIONS_END_TOKEN);
} catch (IOException e) {
Slog.e(TAG, "Failed to write conversations end token to backup payload.", e);
return null;
}
return baos.toByteArray();
}
void restore(@NonNull byte[] payload) {
DataInputStream in = new DataInputStream(new ByteArrayInputStream(payload));
try {
for (int conversationsPayloadSize = in.readInt();
conversationsPayloadSize != CONVERSATIONS_END_TOKEN;
conversationsPayloadSize = in.readInt()) {
byte[] conversationsPayload = new byte[conversationsPayloadSize];
in.readFully(conversationsPayload, 0, conversationsPayloadSize);
String packageName = in.readUTF();
getOrCreatePackageData(packageName).getConversationStore().restore(
conversationsPayload);
}
} catch (IOException e) {
Slog.e(TAG, "Failed to restore conversations from backup payload.", e);
}
}
private PackageData createPackageData(String packageName) {
return new PackageData(packageName, mUserId, this::isDefaultDialer, this::isDefaultSmsApp,
mScheduledExecutorService, mPerUserPeopleDataDir);
}
private boolean isDefaultDialer(String packageName) {
return TextUtils.equals(mDefaultDialer, packageName);
}
private boolean isDefaultSmsApp(String packageName) {
return TextUtils.equals(mDefaultSmsApp, packageName);
}
}