Joe Onorato | 99598ee | 2019-02-11 15:55:13 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2016 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 | #include "Log.h" |
| 18 | |
| 19 | #include "Broadcaster.h" |
| 20 | |
| 21 | #include "IncidentService.h" |
| 22 | |
| 23 | #include <android/os/DropBoxManager.h> |
| 24 | #include <binder/IServiceManager.h> |
Yao Chen | cbafce9 | 2019-04-01 15:56:44 -0700 | [diff] [blame] | 25 | #include <thread> |
Joe Onorato | 99598ee | 2019-02-11 15:55:13 +0000 | [diff] [blame] | 26 | |
| 27 | namespace android { |
| 28 | namespace os { |
| 29 | namespace incidentd { |
| 30 | |
| 31 | using android::os::IIncidentCompanion; |
| 32 | using binder::Status; |
| 33 | |
| 34 | // ============================================================ |
| 35 | Broadcaster::ConsentListener::ConsentListener(const sp<Broadcaster>& broadcaster, |
| 36 | const ReportId& reportId) |
| 37 | :mBroadcaster(broadcaster), |
| 38 | mId(reportId) { |
| 39 | } |
| 40 | |
| 41 | Broadcaster::ConsentListener::~ConsentListener() { |
| 42 | } |
| 43 | |
| 44 | Status Broadcaster::ConsentListener::onReportApproved() { |
| 45 | mBroadcaster->report_approved(mId); |
| 46 | return Status::ok(); |
| 47 | } |
| 48 | |
| 49 | Status Broadcaster::ConsentListener::onReportDenied() { |
| 50 | mBroadcaster->report_denied(mId); |
| 51 | return Status::ok(); |
| 52 | } |
| 53 | |
| 54 | // ============================================================ |
| 55 | Broadcaster::ReportId::ReportId() |
| 56 | :id(), |
| 57 | pkg(), |
| 58 | cls() { |
| 59 | } |
| 60 | |
| 61 | Broadcaster::ReportId::ReportId(const ReportId& that) |
| 62 | :id(that.id), |
| 63 | pkg(that.pkg), |
| 64 | cls(that.cls) { |
| 65 | } |
| 66 | |
| 67 | Broadcaster::ReportId::ReportId(const string& i, const string& p, const string& c) |
| 68 | :id(i), |
| 69 | pkg(p), |
| 70 | cls(c) { |
| 71 | } |
| 72 | |
| 73 | Broadcaster::ReportId::~ReportId() { |
| 74 | } |
| 75 | |
| 76 | bool Broadcaster::ReportId::operator<(const ReportId& that) const { |
| 77 | if (id < that.id) { |
| 78 | return true; |
| 79 | } |
| 80 | if (id > that.id) { |
| 81 | return false; |
| 82 | } |
| 83 | if (pkg < that.pkg) { |
| 84 | return true; |
| 85 | } |
| 86 | if (pkg > that.pkg) { |
| 87 | return false; |
| 88 | } |
| 89 | if (cls < that.cls) { |
| 90 | return true; |
| 91 | } |
| 92 | return false; |
| 93 | } |
| 94 | |
| 95 | // ============================================================ |
| 96 | Broadcaster::ReportStatus::ReportStatus() |
| 97 | :approval_sent(false), |
| 98 | ready_sent(false), |
| 99 | listener(nullptr) { |
| 100 | } |
| 101 | |
| 102 | Broadcaster::ReportStatus::ReportStatus(const ReportStatus& that) |
| 103 | :approval_sent(that.approval_sent), |
| 104 | ready_sent(that.ready_sent), |
| 105 | listener(that.listener) { |
| 106 | } |
| 107 | |
| 108 | Broadcaster::ReportStatus::~ReportStatus() { |
| 109 | } |
| 110 | |
| 111 | // ============================================================ |
| 112 | Broadcaster::Broadcaster(const sp<WorkDirectory>& workDirectory) |
| 113 | :mReportHandler(), |
| 114 | mWorkDirectory(workDirectory) { |
| 115 | } |
| 116 | |
| 117 | void Broadcaster::setHandler(const sp<ReportHandler>& handler) { |
| 118 | mReportHandler = handler; |
| 119 | } |
| 120 | |
| 121 | void Broadcaster::reset() { |
| 122 | unique_lock<mutex> lock(mLock); |
| 123 | mLastSent = 0; |
| 124 | mHistory.clear(); |
| 125 | // Could cancel the listeners, but this happens when |
| 126 | // the system process crashes, so don't bother. |
| 127 | } |
| 128 | |
| 129 | void Broadcaster::clearBroadcasts(const string& pkg, const string& cls, const string& id) { |
| 130 | unique_lock<mutex> lock(mLock); |
| 131 | |
| 132 | map<ReportId,ReportStatus>::const_iterator found = mHistory.find(ReportId(id, pkg, cls)); |
| 133 | if (found != mHistory.end()) { |
| 134 | if (found->second.listener != nullptr) { |
| 135 | sp<IIncidentCompanion> ics = get_incident_companion(); |
| 136 | if (ics != nullptr) { |
| 137 | ics->cancelAuthorization(found->second.listener); |
| 138 | } |
| 139 | } |
| 140 | mHistory.erase(found); |
| 141 | } |
| 142 | } |
| 143 | |
| 144 | void Broadcaster::clearPackageBroadcasts(const string& pkg) { |
| 145 | unique_lock<mutex> lock(mLock); |
| 146 | |
| 147 | map<ReportId,ReportStatus>::iterator it = mHistory.begin(); |
| 148 | while (it != mHistory.end()) { |
| 149 | if (it->first.pkg == pkg) { |
| 150 | if (it->second.listener != nullptr) { |
| 151 | sp<IIncidentCompanion> ics = get_incident_companion(); |
| 152 | if (ics != nullptr) { |
| 153 | ics->cancelAuthorization(it->second.listener); |
| 154 | } |
| 155 | } |
| 156 | it = mHistory.erase(it); |
| 157 | } else { |
| 158 | it++; |
| 159 | } |
| 160 | } |
| 161 | } |
| 162 | |
| 163 | Broadcaster::broadcast_status_t Broadcaster::sendBroadcasts() { |
| 164 | int err; |
| 165 | int64_t lastSent = get_last_sent(); |
| 166 | |
| 167 | vector<sp<ReportFile>> files; |
| 168 | mWorkDirectory->getReports(&files, 0); //lastSent); |
| 169 | |
| 170 | // Don't send multiple broadcasts to the same receiver. |
| 171 | set<ReportId> reportReadyBroadcasts; |
| 172 | |
| 173 | for (const sp<ReportFile>& file: files) { |
| 174 | err = file->loadEnvelope(); |
| 175 | if (err != NO_ERROR) { |
| 176 | ALOGW("Error (%s) loading envelope from %s", strerror(-err), |
| 177 | file->getEnvelopeFileName().c_str()); |
| 178 | continue; |
| 179 | } |
| 180 | |
| 181 | const ReportFileProto& envelope = file->getEnvelope(); |
| 182 | |
| 183 | if (!envelope.completed()) { |
| 184 | ALOGI("Incident report not completed skipping it: %s", |
| 185 | file->getEnvelopeFileName().c_str()); |
| 186 | continue; |
| 187 | } |
| 188 | |
| 189 | // When one of the broadcast functions in this loop fails, it's almost |
| 190 | // certainly because the system process is crashing or has crashed. Rather |
| 191 | // than continuing to pound on the system process and potentially make things |
| 192 | // worse, we bail right away, return BROADCASTS_BACKOFF, and we will try |
| 193 | // again later. In the meantime, if the system process did crash, it might |
| 194 | // clear out mHistory, which means we'll be back here again to send the |
| 195 | // backlog. |
| 196 | size_t reportCount = envelope.report_size(); |
| 197 | bool hasApprovalPending = false; |
| 198 | for (int reportIndex = 0; reportIndex < reportCount; reportIndex++) { |
| 199 | |
| 200 | const ReportFileProto_Report& report = envelope.report(reportIndex); |
| 201 | status_t err; |
| 202 | if (report.privacy_policy() == PRIVACY_POLICY_AUTOMATIC || report.share_approved()) { |
| 203 | // It's privacy policy is AUTO, or it's been approved, |
| 204 | // so send the actual broadcast. |
| 205 | if (!was_ready_sent(file->getId(), report.pkg(), report.cls())) { |
| 206 | if (report.pkg() == DROPBOX_SENTINEL.getPackageName() |
| 207 | && report.cls() == DROPBOX_SENTINEL.getClassName()) { |
| 208 | IncidentReportArgs args; |
| 209 | get_args_from_report(&args, report); |
| 210 | err = send_to_dropbox(file, args); |
| 211 | if (err != NO_ERROR) { |
| 212 | return BROADCASTS_BACKOFF; |
| 213 | } |
| 214 | } else { |
| 215 | reportReadyBroadcasts.insert(ReportId(file->getId(), report.pkg(), |
| 216 | report.cls())); |
| 217 | } |
| 218 | } |
| 219 | } else { |
| 220 | // It's not approved yet, so send the approval. |
| 221 | if (!was_approval_sent(file->getId(), report.pkg(), report.cls())) { |
| 222 | err = send_approval_broadcasts(file->getId(), report.pkg(), report.cls()); |
| 223 | if (err != NO_ERROR) { |
| 224 | return BROADCASTS_BACKOFF; |
| 225 | } |
| 226 | hasApprovalPending = true; |
| 227 | } |
| 228 | } |
| 229 | } |
| 230 | |
| 231 | lastSent = file->getTimestampNs(); |
| 232 | if (!hasApprovalPending) { |
| 233 | set_last_sent(lastSent); |
| 234 | } |
| 235 | } |
| 236 | |
| 237 | for (const ReportId& report: reportReadyBroadcasts) { |
| 238 | err = send_report_ready_broadcasts(report.id, report.pkg, report.cls); |
| 239 | if (err != NO_ERROR) { |
| 240 | return BROADCASTS_BACKOFF; |
| 241 | } |
| 242 | } |
| 243 | |
| 244 | return mWorkDirectory->hasMore(lastSent) ? BROADCASTS_REPEAT : BROADCASTS_FINISHED; |
| 245 | } |
| 246 | |
| 247 | void Broadcaster::set_last_sent(int64_t timestamp) { |
| 248 | unique_lock<mutex> lock(mLock); |
| 249 | mLastSent = timestamp; |
| 250 | } |
| 251 | |
| 252 | int64_t Broadcaster::get_last_sent() { |
| 253 | unique_lock<mutex> lock(mLock); |
| 254 | return mLastSent; |
| 255 | } |
| 256 | |
| 257 | /* |
| 258 | void Broadcaster::printReportStatuses() const { |
| 259 | ALOGD("mHistory {"); |
| 260 | for (map<ReportId,ReportStatus>::const_iterator it = mHistory.begin(); |
| 261 | it != mHistory.end(); it++) { |
| 262 | ALOGD(" [%s %s] --> [%d %d]", it->first.id.c_str(), it->first.pkg.c_str(), |
| 263 | it->second.approval_sent, it->second.ready_sent); |
| 264 | } |
| 265 | ALOGD("}"); |
| 266 | } |
| 267 | */ |
| 268 | |
| 269 | bool Broadcaster::was_approval_sent(const string& id, const string& pkg, const string& cls) { |
| 270 | unique_lock<mutex> lock(mLock); |
| 271 | map<ReportId,ReportStatus>::const_iterator found = mHistory.find(ReportId(id, pkg, cls)); |
| 272 | if (found != mHistory.end()) { |
| 273 | return found->second.approval_sent; |
| 274 | } |
| 275 | return false; |
| 276 | } |
| 277 | |
| 278 | void Broadcaster::set_approval_sent(const string& id, const string& pkg, const string& cls, |
| 279 | const sp<ConsentListener>& listener) { |
| 280 | unique_lock<mutex> lock(mLock); |
| 281 | ReportStatus& reportStatus = mHistory[ReportId(id, pkg, cls)]; |
| 282 | reportStatus.approval_sent = true; |
| 283 | reportStatus.listener = listener; |
| 284 | } |
| 285 | |
| 286 | bool Broadcaster::was_ready_sent(const string& id, const string& pkg, const string& cls) { |
| 287 | unique_lock<mutex> lock(mLock); |
| 288 | map<ReportId,ReportStatus>::const_iterator found = mHistory.find(ReportId(id, pkg, cls)); |
| 289 | if (found != mHistory.end()) { |
| 290 | return found->second.ready_sent; |
| 291 | } |
| 292 | return false; |
| 293 | } |
| 294 | |
| 295 | void Broadcaster::set_ready_sent(const string& id, const string& pkg, const string& cls) { |
| 296 | unique_lock<mutex> lock(mLock); |
| 297 | mHistory[ReportId(id, pkg, cls)].ready_sent = true; |
| 298 | } |
| 299 | |
| 300 | status_t Broadcaster::send_approval_broadcasts(const string& id, const string& pkg, |
| 301 | const string& cls) { |
| 302 | sp<IIncidentCompanion> ics = get_incident_companion(); |
| 303 | if (ics == nullptr) { |
| 304 | return NAME_NOT_FOUND; |
| 305 | } |
| 306 | |
| 307 | sp<ConsentListener> listener = new ConsentListener(this, ReportId(id, pkg, cls)); |
| 308 | |
| 309 | ALOGI("send_approval_broadcasts for %s %s/%s", id.c_str(), pkg.c_str(), cls.c_str()); |
| 310 | |
| 311 | Status status = ics->authorizeReport(0, String16(pkg.c_str()), |
| 312 | String16(cls.c_str()), String16(id.c_str()), 0, listener); |
| 313 | |
| 314 | if (!status.isOk()) { |
| 315 | // authorizeReport is oneway, so any error is a transaction error. |
| 316 | return status.transactionError(); |
| 317 | } |
| 318 | |
| 319 | set_approval_sent(id, pkg, cls, listener); |
| 320 | |
| 321 | return NO_ERROR; |
| 322 | } |
| 323 | |
| 324 | void Broadcaster::report_approved(const ReportId& reportId) { |
| 325 | status_t err; |
| 326 | |
| 327 | // Kick off broadcaster to do send the ready broadcasts. |
| 328 | ALOGI("The user approved the report, so kicking off another broadcast pass. %s %s/%s", |
| 329 | reportId.id.c_str(), reportId.pkg.c_str(), reportId.cls.c_str()); |
| 330 | sp<ReportFile> file = mWorkDirectory->getReport(reportId.pkg, reportId.cls, reportId.id, |
| 331 | nullptr); |
| 332 | if (file != nullptr) { |
| 333 | err = file->loadEnvelope(); |
| 334 | if (err != NO_ERROR) { |
| 335 | return; |
| 336 | } |
| 337 | |
| 338 | err = file->markApproved(reportId.pkg, reportId.cls); |
| 339 | if (err != NO_ERROR) { |
| 340 | ALOGI("Couldn't find report that was just approved: %s %s/%s", |
| 341 | reportId.id.c_str(), reportId.pkg.c_str(), reportId.cls.c_str()); |
| 342 | return; |
| 343 | } |
| 344 | |
| 345 | file->saveEnvelope(); |
| 346 | if (err != NO_ERROR) { |
| 347 | return; |
| 348 | } |
| 349 | } |
| 350 | mReportHandler->scheduleSendBacklog(); |
| 351 | } |
| 352 | |
| 353 | void Broadcaster::report_denied(const ReportId& reportId) { |
| 354 | // The user didn't approve the report, so remove it from the WorkDirectory. |
| 355 | ALOGI("The user denied the report, so deleting it. %s %s/%s", |
| 356 | reportId.id.c_str(), reportId.pkg.c_str(), reportId.cls.c_str()); |
| 357 | sp<ReportFile> file = mWorkDirectory->getReport(reportId.pkg, reportId.cls, reportId.id, |
| 358 | nullptr); |
| 359 | if (file != nullptr) { |
| 360 | mWorkDirectory->commit(file, reportId.pkg, reportId.cls); |
| 361 | } |
| 362 | } |
| 363 | |
| 364 | status_t Broadcaster::send_report_ready_broadcasts(const string& id, const string& pkg, |
| 365 | const string& cls) { |
| 366 | sp<IIncidentCompanion> ics = get_incident_companion(); |
| 367 | if (ics == nullptr) { |
| 368 | return NAME_NOT_FOUND; |
| 369 | } |
| 370 | |
| 371 | ALOGI("send_report_ready_broadcasts for %s %s/%s", id.c_str(), pkg.c_str(), cls.c_str()); |
| 372 | |
| 373 | Status status = ics->sendReportReadyBroadcast(String16(pkg.c_str()), String16(cls.c_str())); |
| 374 | |
| 375 | if (!status.isOk()) { |
| 376 | // sendReportReadyBroadcast is oneway, so any error is a transaction error. |
| 377 | return status.transactionError(); |
| 378 | } |
| 379 | |
| 380 | set_ready_sent(id, pkg, cls); |
| 381 | |
| 382 | return NO_ERROR; |
| 383 | } |
| 384 | |
| 385 | status_t Broadcaster::send_to_dropbox(const sp<ReportFile>& file, |
| 386 | const IncidentReportArgs& args) { |
| 387 | status_t err; |
| 388 | |
| 389 | sp<DropBoxManager> dropbox = new DropBoxManager(); |
| 390 | if (dropbox == nullptr) { |
| 391 | ALOGW("Can't reach dropbox now, so we won't be able to write the incident report to there"); |
| 392 | return NO_ERROR; |
| 393 | } |
| 394 | |
Yao Chen | cbafce9 | 2019-04-01 15:56:44 -0700 | [diff] [blame] | 395 | int fds[2]; |
| 396 | if (pipe(fds) != 0) { |
| 397 | ALOGW("Error opening pipe to filter incident report: %s", file->getDataFileName().c_str()); |
| 398 | return NO_ERROR; |
Joe Onorato | 99598ee | 2019-02-11 15:55:13 +0000 | [diff] [blame] | 399 | } |
| 400 | |
Yao Chen | cbafce9 | 2019-04-01 15:56:44 -0700 | [diff] [blame] | 401 | int readFd = fds[0]; |
| 402 | int writeFd = fds[1]; |
| 403 | |
| 404 | // spawn a thread to write the data. Release the writeFd ownership to the thread. |
| 405 | thread th([file, writeFd, args]() { file->startFilteringData(writeFd, args); }); |
| 406 | |
| 407 | th.detach(); |
| 408 | |
Joe Onorato | 99598ee | 2019-02-11 15:55:13 +0000 | [diff] [blame] | 409 | // Takes ownership of readFd. |
| 410 | Status status = dropbox->addFile(String16("incident"), readFd, 0); |
| 411 | if (!status.isOk()) { |
| 412 | // TODO: This may or may not leak the readFd, depending on where it failed. |
| 413 | // Not sure how to fix this given the dropbox API. |
| 414 | ALOGW("Error sending incident report to dropbox."); |
| 415 | return -errno; |
| 416 | } |
| 417 | |
| 418 | // On successful write, tell the working directory that this file is done. |
| 419 | mWorkDirectory->commit(file, DROPBOX_SENTINEL.getPackageName(), |
| 420 | DROPBOX_SENTINEL.getClassName()); |
| 421 | |
| 422 | // Don't need to call set_ready_sent, because we just removed it from the ReportFile, |
| 423 | // so we'll never hear about it again. |
| 424 | |
| 425 | return NO_ERROR; |
| 426 | } |
| 427 | |
| 428 | sp<IIncidentCompanion> Broadcaster::get_incident_companion() { |
| 429 | sp<IBinder> binder = defaultServiceManager()->getService(String16("incidentcompanion")); |
| 430 | if (binder == nullptr) { |
| 431 | ALOGI("Can not find IIncidentCompanion service to send broadcast. Will try again later."); |
| 432 | return nullptr; |
| 433 | } |
| 434 | |
| 435 | sp<IIncidentCompanion> ics = interface_cast<IIncidentCompanion>(binder); |
| 436 | if (ics == nullptr) { |
| 437 | ALOGI("The incidentcompanion service is not an IIncidentCompanion. Will try again later."); |
| 438 | return nullptr; |
| 439 | } |
| 440 | |
| 441 | return ics; |
| 442 | } |
| 443 | |
| 444 | } // namespace incidentd |
| 445 | } // namespace os |
| 446 | } // namespace android |
| 447 | |
| 448 | |