• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 Google LLC
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 package com.google.android.libraries.mobiledatadownload.internal.logging;
17 
18 import static com.google.common.util.concurrent.Futures.immediateFuture;
19 
20 import com.google.android.libraries.mobiledatadownload.internal.FileGroupManager;
21 import com.google.android.libraries.mobiledatadownload.internal.FileGroupManager.GroupDownloadStatus;
22 import com.google.android.libraries.mobiledatadownload.internal.FileGroupsMetadata;
23 import com.google.android.libraries.mobiledatadownload.internal.annotations.SequentialControlExecutor;
24 import com.google.android.libraries.mobiledatadownload.internal.collect.GroupKeyAndGroup;
25 import com.google.android.libraries.mobiledatadownload.internal.util.FileGroupUtil;
26 import com.google.android.libraries.mobiledatadownload.tracing.PropagatedFutures;
27 import com.google.common.util.concurrent.Futures;
28 import com.google.common.util.concurrent.ListenableFuture;
29 import com.google.mobiledatadownload.LogEnumsProto.MddFileGroupDownloadStatus;
30 import com.google.mobiledatadownload.LogProto.DataDownloadFileGroupStats;
31 import com.google.mobiledatadownload.LogProto.MddFileGroupStatus;
32 import com.google.mobiledatadownload.internal.MetadataProto.DataFileGroupInternal;
33 import com.google.mobiledatadownload.internal.MetadataProto.GroupKey;
34 import java.util.ArrayList;
35 import java.util.List;
36 import java.util.concurrent.Executor;
37 import javax.inject.Inject;
38 
39 /**
40  * Log MDD file group stats. For each file group, it will log the file group details along with the
41  * current state of the file group (pending, downloaded or stale).
42  */
43 public class FileGroupStatsLogger {
44 
45   private static final String TAG = "FileGroupStatsLogger";
46   private final FileGroupManager fileGroupManager;
47   private final FileGroupsMetadata fileGroupsMetadata;
48   private final EventLogger eventLogger;
49   private final Executor sequentialControlExecutor;
50 
51   @Inject
FileGroupStatsLogger( FileGroupManager fileGroupManager, FileGroupsMetadata fileGroupsMetadata, EventLogger eventLogger, @SequentialControlExecutor Executor sequentialControlExecutor)52   public FileGroupStatsLogger(
53       FileGroupManager fileGroupManager,
54       FileGroupsMetadata fileGroupsMetadata,
55       EventLogger eventLogger,
56       @SequentialControlExecutor Executor sequentialControlExecutor) {
57     this.fileGroupManager = fileGroupManager;
58     this.fileGroupsMetadata = fileGroupsMetadata;
59     this.eventLogger = eventLogger;
60     this.sequentialControlExecutor = sequentialControlExecutor;
61   }
62 
63   // TODO(b/73490689): Also log stats about stale groups.
log(int daysSinceLastLog)64   public ListenableFuture<Void> log(int daysSinceLastLog) {
65     return eventLogger.logMddFileGroupStats(() -> buildFileGroupStatusList(daysSinceLastLog));
66   }
67 
buildFileGroupStatusList( int daysSinceLastLog)68   private ListenableFuture<List<EventLogger.FileGroupStatusWithDetails>> buildFileGroupStatusList(
69       int daysSinceLastLog) {
70     return PropagatedFutures.transformAsync(
71         fileGroupsMetadata.getAllFreshGroups(),
72         downloadedAndPendingGroups -> {
73           List<ListenableFuture<EventLogger.FileGroupStatusWithDetails>> futures =
74               new ArrayList<>();
75           for (GroupKeyAndGroup pair : downloadedAndPendingGroups) {
76             GroupKey groupKey = pair.groupKey();
77             DataFileGroupInternal dataFileGroup = pair.dataFileGroup();
78             if (dataFileGroup == null) {
79               continue;
80             }
81 
82             DataDownloadFileGroupStats fileGroupDetails =
83                 DataDownloadFileGroupStats.newBuilder()
84                     .setFileGroupName(groupKey.getGroupName())
85                     .setOwnerPackage(groupKey.getOwnerPackage())
86                     .setFileGroupVersionNumber(dataFileGroup.getFileGroupVersionNumber())
87                     .setFileCount(dataFileGroup.getFileCount())
88                     .setInlineFileCount(FileGroupUtil.getInlineFileCount(dataFileGroup))
89                     .setHasAccount(!groupKey.getAccount().isEmpty())
90                     .setBuildId(dataFileGroup.getBuildId())
91                     .setVariantId(dataFileGroup.getVariantId())
92                     .build();
93 
94             futures.add(
95                 PropagatedFutures.transform(
96                     buildFileGroupStatus(dataFileGroup, groupKey, daysSinceLastLog),
97                     fileGroupStatus ->
98                         EventLogger.FileGroupStatusWithDetails.create(
99                             fileGroupStatus, fileGroupDetails),
100                     sequentialControlExecutor));
101           }
102           return Futures.allAsList(futures);
103         },
104         sequentialControlExecutor);
105   }
106 
buildFileGroupStatus( DataFileGroupInternal dataFileGroup, GroupKey groupKey, int daysSinceLastLog)107   private ListenableFuture<MddFileGroupStatus> buildFileGroupStatus(
108       DataFileGroupInternal dataFileGroup, GroupKey groupKey, int daysSinceLastLog) {
109     MddFileGroupStatus.Builder fileGroupStatus =
110         MddFileGroupStatus.newBuilder().setDaysSinceLastLog(daysSinceLastLog);
111     if (dataFileGroup.getBookkeeping().hasGroupNewFilesReceivedTimestamp()) {
112       fileGroupStatus.setGroupAddedTimestampInSeconds(
113           dataFileGroup.getBookkeeping().getGroupNewFilesReceivedTimestamp() / 1000);
114     } else {
115       fileGroupStatus.setGroupAddedTimestampInSeconds(-1);
116     }
117 
118     if (groupKey.getDownloaded()) {
119       fileGroupStatus.setFileGroupDownloadStatus(MddFileGroupDownloadStatus.Code.COMPLETE);
120       if (dataFileGroup.getBookkeeping().hasGroupDownloadedTimestampInMillis()) {
121         fileGroupStatus.setGroupDownloadedTimestampInSeconds(
122             dataFileGroup.getBookkeeping().getGroupDownloadedTimestampInMillis() / 1000);
123       } else {
124         fileGroupStatus.setGroupDownloadedTimestampInSeconds(-1);
125       }
126       return immediateFuture(fileGroupStatus.build());
127     } else {
128       fileGroupStatus.setGroupDownloadedTimestampInSeconds(-1);
129       return PropagatedFutures.transform(
130           fileGroupManager.getFileGroupDownloadStatus(dataFileGroup),
131           status -> {
132             if (status == GroupDownloadStatus.DOWNLOADED || status == GroupDownloadStatus.PENDING) {
133               // Log pending even if verify returns downloaded, as it will be marked as
134               // completed in the next periodic task.
135               fileGroupStatus.setFileGroupDownloadStatus(MddFileGroupDownloadStatus.Code.PENDING);
136             } else {
137               // TODO(b/73490689): Log the reason for failure along with this.
138               fileGroupStatus.setFileGroupDownloadStatus(MddFileGroupDownloadStatus.Code.FAILED);
139             }
140             return fileGroupStatus.build();
141           },
142           sequentialControlExecutor);
143     }
144   }
145 }
146