1/*
2 * Copyright (C) 2015 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#define LOG_TAG "KeystoreOperation"
17
18#include "operation.h"
19
20#include <algorithm>
21
22namespace keystore {
23using namespace android;
24
25OperationMap::OperationMap(IBinder::DeathRecipient* deathRecipient)
26    : mDeathRecipient(deathRecipient) {}
27
28sp<IBinder> OperationMap::addOperation(uint64_t handle, uint64_t keyid, KeyPurpose purpose,
29                                       const OperationMap::km_device_t& dev,
30                                       const sp<IBinder>& appToken,
31                                       KeyCharacteristics&& characteristics, bool pruneable) {
32    sp<IBinder> token = new BBinder();
33    mMap[token] = Operation(handle, keyid, purpose, dev, std::move(characteristics), appToken);
34    if (pruneable) {
35        mLru.push_back(token);
36    }
37    if (mAppTokenMap.find(appToken) == mAppTokenMap.end()) {
38        appToken->linkToDeath(mDeathRecipient);
39    }
40    mAppTokenMap[appToken].push_back(token);
41    return token;
42}
43
44bool OperationMap::getOperation(const sp<IBinder>& token, uint64_t* outHandle, uint64_t* outKeyid,
45                                KeyPurpose* outPurpose, km_device_t* outDevice,
46                                const KeyCharacteristics** outCharacteristics) {
47    if (!outHandle || !outDevice) {
48        return false;
49    }
50    auto entry = mMap.find(token);
51    if (entry == mMap.end()) {
52        return false;
53    }
54    updateLru(token);
55
56    *outHandle = entry->second.handle;
57    *outKeyid = entry->second.keyid;
58    *outPurpose = entry->second.purpose;
59    *outDevice = entry->second.device;
60    if (outCharacteristics) {
61        *outCharacteristics = &entry->second.characteristics;
62    }
63    return true;
64}
65
66void OperationMap::updateLru(const sp<IBinder>& token) {
67    auto lruEntry = std::find(mLru.begin(), mLru.end(), token);
68    if (lruEntry != mLru.end()) {
69        mLru.erase(lruEntry);
70        mLru.push_back(token);
71    }
72}
73
74bool OperationMap::removeOperation(const sp<IBinder>& token) {
75    auto entry = mMap.find(token);
76    if (entry == mMap.end()) {
77        return false;
78    }
79    sp<IBinder> appToken = entry->second.appToken;
80    mMap.erase(entry);
81    auto lruEntry = std::find(mLru.begin(), mLru.end(), token);
82    if (lruEntry != mLru.end()) {
83        mLru.erase(lruEntry);
84    }
85    removeOperationTracking(token, appToken);
86    return true;
87}
88
89void OperationMap::removeOperationTracking(const sp<IBinder>& token, const sp<IBinder>& appToken) {
90    auto appEntry = mAppTokenMap.find(appToken);
91    if (appEntry == mAppTokenMap.end()) {
92        ALOGE("Entry for %p contains unmapped application token %p", token.get(), appToken.get());
93        return;
94    }
95    auto tokenEntry = std::find(appEntry->second.begin(), appEntry->second.end(), token);
96    appEntry->second.erase(tokenEntry);
97    // Stop listening for death if all operations tied to the token have finished.
98    if (appEntry->second.size() == 0) {
99        appToken->unlinkToDeath(mDeathRecipient);
100        mAppTokenMap.erase(appEntry);
101    }
102}
103
104bool OperationMap::hasPruneableOperation() const {
105    return mLru.size() != 0;
106}
107
108size_t OperationMap::getPruneableOperationCount() const {
109    return mLru.size();
110}
111
112sp<IBinder> OperationMap::getOldestPruneableOperation() {
113    if (!hasPruneableOperation()) {
114        return sp<IBinder>(NULL);
115    }
116    return mLru[0];
117}
118
119bool OperationMap::getOperationAuthToken(const sp<IBinder>& token,
120                                         const HardwareAuthToken** outToken) {
121    auto entry = mMap.find(token);
122    if (entry == mMap.end()) {
123        return false;
124    }
125    *outToken = entry->second.authToken.get();
126    return true;
127}
128
129bool OperationMap::setOperationAuthToken(const sp<IBinder>& token,
130                                         const HardwareAuthToken* authToken) {
131    auto entry = mMap.find(token);
132    if (entry == mMap.end()) {
133        return false;
134    }
135    entry->second.authToken.reset(new HardwareAuthToken);
136    *entry->second.authToken = *authToken;
137    return true;
138}
139
140std::vector<sp<IBinder>> OperationMap::getOperationsForToken(const sp<IBinder>& appToken) {
141    auto appEntry = mAppTokenMap.find(appToken);
142    if (appEntry != mAppTokenMap.end()) {
143        return appEntry->second;
144    } else {
145        return std::vector<sp<IBinder>>();
146    }
147}
148
149OperationMap::Operation::Operation(uint64_t handle_, uint64_t keyid_, KeyPurpose purpose_,
150                                   const OperationMap::km_device_t& device_,
151                                   KeyCharacteristics&& characteristics_, sp<IBinder> appToken_)
152    : handle(handle_), keyid(keyid_), purpose(purpose_), device(device_),
153      characteristics(characteristics_), appToken(appToken_) {}
154
155OperationMap::Operation::Operation()
156    : handle(0), keyid(0), device(nullptr), characteristics(), appToken(nullptr) {}
157
158}  // namespace android
159