• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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 #pragma once
18 
19 #include <android-base/thread_annotations.h>
20 #include <com/android/internal/app/BnAppOpsCallback.h>
21 #include <cutils/android_filesystem_config.h>
22 #include <log/log.h>
23 #include <utils/RefBase.h>
24 
25 #include <functional>
26 
27 #include "media/ValidatedAttributionSourceState.h"
28 
29 namespace android::media::permission {
30 
31 using ValidatedAttributionSourceState =
32         com::android::media::permission::ValidatedAttributionSourceState;
33 
34 struct Ops {
35     int attributedOp = -1;  // same as OP_NONE
36     int additionalOp = -1;
37 };
38 
39 /**
40  * This session manages an ongoing data access corresponding with appops.
41  *
42  * This access can be temporarily stopped by appops or the data source. When access is revoked by
43  * AppOps, the registered callback will be called in order to ensure that the data delivery is
44  * halted. When halted by the data source, AppOps will be notified that the access ended.
45  * Note, this session does not ref-count on itself. It should represent a single access, which
46  * necessarily cannot nest.
47  * This class is fully locked since notifications from appops are async. Public interface can be
48  * slow due to binder calls.
49  */
50 template <typename AppOpsFacade>
51 // Abstract interface that permits minor differences in how appops is called per client usage
requires(AppOpsFacade x,const ValidatedAttributionSourceState attr)52     requires requires(AppOpsFacade x, const ValidatedAttributionSourceState attr) {
53         { x.startAccess(attr, Ops{}) } -> std::same_as<bool>;  // true if permitted
54         { x.stopAccess(attr, Ops{}) } -> std::same_as<void>;
55         { x.checkAccess(attr, Ops{}) } -> std::same_as<bool>;  // true if permitted
56         {
57             x.addChangeCallback(attr, Ops{}, std::function<void(bool)>{})
58         } -> std::same_as<uintptr_t>;
59         // no more calls after return is required
60         { x.removeChangeCallback(uintptr_t{}) } -> std::same_as<void>;
61     }
62 class AppOpsSession {
63   public:
64     /**
65      * @param attr - AttributionChain which the access is attributed to.
66      * @param ops - The ops required for this delivery
67      * @param opChangedCb - A callback (async) which  notifies the data source that the permitted
68      * state due to appops has changed. This is only called if a delivery request is ongoing (i.e.
69      * after a `beginDeliveryRequest` but before a `endDeliveryRequest`, regardless of the return
70      * value of the former). Upon calling the cb, appops has been updated, so the post-condition is
71      * that the data source delivers data iff the parameter is true. If the delivery fails for some
72      * reason, `endDeliveryRequest` should be called shortly, however, there is no re-entrancy into
73      * this class. The client should never change the access request state based on this cb.
74      * @param appOpsFacade - See the requires clause -- an interface which encapsulates the calls to
75      * AppOpsService.
76      */
77     AppOpsSession(ValidatedAttributionSourceState attr, Ops ops,
78                   std::function<void(bool)> opChangedCb, AppOpsFacade appOpsFacade = {})
mAttr(std::move (attr))79         : mAttr(std::move(attr)),
80           mOps(ops),
81           mCb(std::move(opChangedCb)),
82           mAppOps(std::move(appOpsFacade)),
83           mCookie(mAppOps.addChangeCallback(mAttr, ops,
84                                             [this](bool x) { this->onPermittedChanged(x); })),
85           mDeliveryRequested(false),
86           mDeliveryPermitted(mAppOps.checkAccess(mAttr, ops)) { }
87 
~AppOpsSession()88     ~AppOpsSession() {
89         endDeliveryRequest();
90         mAppOps.removeChangeCallback(mCookie);
91     }
92 
93     /**
94      * Source intends to start delivering data. Updates AppOps if applicable.
95      * @return true if data should be delivered (i.e. AppOps also permits delivery)
96      */
beginDeliveryRequest()97     bool beginDeliveryRequest() {
98         std::lock_guard l{mLock};
99         if (mDeliveryRequested) {
100             ALOG(LOG_WARN, "AppOpsSession", "Redundant beginDeliveryRequest ignored");
101             return mDeliveryPermitted;
102         }
103         mDeliveryRequested = true;
104         if (mDeliveryPermitted) {
105             mDeliveryPermitted = mAppOps.startAccess(mAttr, mOps);
106         }
107         return mDeliveryPermitted;
108     }
109 
110     /**
111      * Source intends to stop delivering data. Updates AppOps if applicable.
112      */
endDeliveryRequest()113     void endDeliveryRequest() {
114         std::lock_guard l{mLock};
115         if (!mDeliveryRequested) return;
116         mDeliveryRequested = false;
117         if (mDeliveryPermitted) {
118             mAppOps.stopAccess(mAttr, mOps);
119         }
120     }
121 
122     /**
123      * Check if delivery is permitted.
124      */
isDeliveryPermitted()125     bool isDeliveryPermitted() const {
126         std::lock_guard l{mLock};
127         return mDeliveryPermitted;
128     }
129 
130   private:
131     /**
132      * AppOps permitted state has changed. From callback thread.
133      */
onPermittedChanged(bool isPermitted)134     void onPermittedChanged(bool isPermitted) {
135         std::lock_guard l{mLock};
136         if (mDeliveryPermitted == isPermitted) return;
137         const bool oldIsPermitted = mDeliveryPermitted;
138         mDeliveryPermitted = isPermitted;
139         if (!mDeliveryRequested) return;
140         if (mDeliveryPermitted) {
141             mDeliveryPermitted = mAppOps.startAccess(mAttr, mOps);
142         } else {
143             mAppOps.stopAccess(mAttr, mOps);
144         }
145         if (oldIsPermitted != mDeliveryPermitted) {
146             mCb(mDeliveryPermitted);
147         }
148     }
149 
150     mutable std::mutex mLock{};
151     const ValidatedAttributionSourceState mAttr;
152     const Ops mOps;
153     const std::function<void(bool)> mCb;
154     AppOpsFacade mAppOps GUARDED_BY(mLock);
155     const uintptr_t mCookie;
156     bool mDeliveryRequested GUARDED_BY(mLock);
157     bool mDeliveryPermitted GUARDED_BY(mLock);
158 };
159 
160 class DefaultAppOpsFacade {
161   public:
162     bool startAccess(const ValidatedAttributionSourceState&, Ops);
163     void stopAccess(const ValidatedAttributionSourceState&, Ops);
164     bool checkAccess(const ValidatedAttributionSourceState&, Ops);
165     uintptr_t addChangeCallback(const ValidatedAttributionSourceState&, Ops,
166                                 std::function<void(bool)> cb);
167     void removeChangeCallback(uintptr_t);
168 
169     class OpMonitor : public com::android::internal::app::BnAppOpsCallback {
170       public:
OpMonitor(ValidatedAttributionSourceState attr,Ops ops,std::function<void (bool)> cb)171         OpMonitor(ValidatedAttributionSourceState attr, Ops ops, std::function<void(bool)> cb)
172             : mAttr(std::move(attr)), mOps(ops), mCb(std::move(cb)) { }
173 
174         binder::Status opChanged(int32_t op, int32_t uid, const String16& packageName,
175                                  const String16& persistenDeviceId) override;
176 
stopListening()177         void stopListening() {
178             std::lock_guard l_{mLock};
179             mCb = nullptr;
180         }
181 
182       private:
183         const ValidatedAttributionSourceState mAttr;
184         const Ops mOps;
185         std::mutex mLock;
186         std::function<void(bool)> mCb GUARDED_BY(mLock);
187     };
188 
189   private:
190     static inline std::mutex sMapLock{};
191     static inline std::unordered_map<uintptr_t, sp<OpMonitor>> sCbMap{};
192 };
193 
194 }  // namespace android::media::permission
195