sbDeviceEnsureSpaceForWrite.cpp
Go to the documentation of this file.
1 /* vim: set sw=2 :miv */
2 /*
3  *=BEGIN SONGBIRD GPL
4  *
5  * This file is part of the Songbird web player.
6  *
7  * Copyright(c) 2005-2011 POTI, Inc.
8  * http://www.songbirdnest.com
9  *
10  * This file may be licensed under the terms of of the
11  * GNU General Public License Version 2 (the ``GPL'').
12  *
13  * Software distributed under the License is distributed
14  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
15  * express or implied. See the GPL for the specific language
16  * governing rights and limitations.
17  *
18  * You should have received a copy of the GPL along with this
19  * program. If not, go to http://www.gnu.org/licenses/gpl.html
20  * or write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22  *
23  *=END SONGBIRD GPL
24  */
25 
27 
28 // Local includes
29 #include "sbDeviceUtils.h"
30 
31 // Standard includes
32 #include <algorithm>
33 #include <set>
34 #include <ctime>
35 
36 // Mozilla interfaces
37 #include <nsIPropertyBag2.h>
38 #include <nsIVariant.h>
39 
40 // Mozilla includes
41 #include <nsArrayUtils.h>
42 
43 // Songbird interfaces
44 #include <sbIDeviceEvent.h>
45 #include <sbIDeviceProperties.h>
46 
47 // Songbird includes
48 #include <sbLibraryUtils.h>
50 #include <sbStringUtils.h>
51 #include <sbVariantUtils.h>
52 #include <sbILibraryChangeset.h>
53 
54 /*
55  * To log this module, set the following environment variable:
56  * NSPR_LOG_MODULES=sbBaseDevice:5
57  */
58 #ifdef PR_LOGGING
59 extern PRLogModuleInfo* gBaseDeviceLog;
60 #endif
61 
62 #undef LOG
63 #define LOG(args) PR_LOG(gBaseDeviceLog, PR_LOG_WARN, args)
64 
66  sbBaseDevice * aDevice,
67  sbIDeviceLibrary * aDevLibrary,
68  sbILibraryChangeset * aChangeset) :
69  mDevice(aDevice),
70  mDevLibrary(aDevLibrary),
71  mChangeset(aChangeset),
72  mTotalLength(0),
73  mFreeSpace(0) {
74 }
75 
77 }
78 
79 nsresult
80 sbDeviceEnsureSpaceForWrite::GetFreeSpace() {
81  nsresult rv;
82  nsAutoString freeSpaceStr;
83  rv = mDevLibrary->GetProperty(NS_LITERAL_STRING(SB_DEVICE_PROPERTY_FREE_SPACE),
84  freeSpaceStr);
85  NS_ENSURE_SUCCESS(rv, rv);
86  mFreeSpace = nsString_ToInt64(freeSpaceStr, &rv);
87  NS_ENSURE_SUCCESS(rv, rv);
88 
89  // apply limit to the total space available for music
90  PRInt64 freeMusicSpace;
91  rv = mDevice->GetMusicFreeSpace(mDevLibrary, &freeMusicSpace);
92  NS_ENSURE_SUCCESS(rv, rv);
93  if (mFreeSpace >= freeMusicSpace)
94  mFreeSpace = freeMusicSpace;
95 
96  return NS_OK;
97 }
98 
99 nsresult
100 sbDeviceEnsureSpaceForWrite::RemoveExtraItems() {
101  nsresult rv;
102 
103  /* We'll go through the changes and separate into ADDEDs and 'others', which
104  * should be MODIFIEDs. We'll handle the 'others' first to ensure that
105  * things like modifications are all done before additions to the device */
106  nsCOMPtr<nsIMutableArray> addChanges =
107  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
108  NS_ENSURE_SUCCESS(rv, rv);
109 
110  nsCOMPtr<nsIMutableArray> otherChanges =
111  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
112  NS_ENSURE_SUCCESS(rv, rv);
113 
114  // Get all of the changes in our changeset
115  nsCOMPtr<nsIArray> changes;
116  rv = mChangeset->GetChanges(getter_AddRefs(changes));
117  NS_ENSURE_SUCCESS(rv, rv);
118 
119  /* Loop over the changes in the changeset, separate into ADDED and 'other',
120  * and calculate how much space all of the changes will take */
121  PRUint32 length;
122  rv = changes->GetLength(&length);
123  NS_ENSURE_SUCCESS(rv, rv);
124  PRInt64 totalChangeSize = 0;
125  for (PRUint32 i = 0; i < length; i++) {
126  nsCOMPtr<sbILibraryChange> change = do_QueryElementAt(changes, i, &rv);
127  NS_ENSURE_SUCCESS(rv, rv);
128 
129  PRUint32 operation;
130  rv = change->GetOperation(&operation);
131  NS_ENSURE_SUCCESS(rv, 0);
132 
133  // Separate changes into ADDED and other
134  if (operation == sbIChangeOperation::ADDED) {
135  addChanges->AppendElement(change, PR_FALSE);
136  }
137  else {
138  otherChanges->AppendElement(change, PR_FALSE);
139  }
140 
141  // Keep track of how much space all of these changes will take
142  PRInt64 changeSize = mDevice->GetChangeSize(mDevLibrary, change);
143  totalChangeSize += changeSize;
144  }
145 
146  // Check if the changes will fit on the device as is
147  if (totalChangeSize >= mFreeSpace) {
148  // not everything fits, see what the user wants to do
149  if (!mDevice->GetEnsureSpaceChecked()) {
150  PRBool abort;
152  mDevLibrary,
153  totalChangeSize,
154  mFreeSpace,
155  &abort);
156  NS_ENSURE_SUCCESS(rv, rv);
157  if (abort) {
158  // If the user aborts, we bail
159  return NS_ERROR_ABORT;
160  }
161 
162  /* If the user does not abort, we log that he/she has agreed to the
163  * EnsureSpaceCheck and continue to find out which changes will fit */
164  mDevice->SetEnsureSpaceChecked(true);
165  }
166  }
167  else {
168  // everything fits, we're good
169  return NS_OK;
170  }
171 
172  // an array of the changes we'll actually complete
173  nsCOMPtr<nsIMutableArray> changesToTake =
174  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
175  PRInt64 changesToTakeSize = 0;
176 
177  /* We attempt to add the 'other', non-ADDED, changes first because they will
178  * likely not take much space or, in the case of DELETEs, add free space to
179  * the device whereas all ADDED changes will reduce the free space
180  * significantly */
181  PRUint32 otherChangesLength;
182  rv = otherChanges->GetLength(&otherChangesLength);
183  for (PRUint32 i = 0; i < otherChangesLength; i++) {
184  nsCOMPtr<sbILibraryChange> change = do_QueryElementAt(otherChanges, i, &rv);
185  NS_ENSURE_SUCCESS(rv, rv);
186 
187  /* See how big the change is, if it will fit, add it to the list of changes
188  * that we'll actually perform */
189  PRInt64 changeSize = mDevice->GetChangeSize(mDevLibrary, change);
190  if ((changesToTakeSize + changeSize) < mFreeSpace) {
191  changesToTake->AppendElement(change, PR_FALSE);
192  changesToTakeSize += changeSize;
193  }
194  }
195 
196  // Seed our random number generator
197  time_t t;
198  time(&t);
199  srand((unsigned int) t);
200 
201  // Now we consider our ADDED changes in random order
202  PRUint32 addChangesLength;
203  rv = addChanges->GetLength(&addChangesLength);
204  while (addChangesLength != 0) {
205  // Get a semi-random number between 0 and addChangesLength
206  PRUint32 random = (unsigned int) ((rand() / (RAND_MAX + 1.0)) * addChangesLength);
207 
208  nsCOMPtr<sbILibraryChange> change = do_QueryElementAt(addChanges, random, &rv);
209  NS_ENSURE_SUCCESS(rv, rv);
210 
211  /* We are considering this change currently, so remove it from our list
212  * of ADDED changes to be considered. */
213  addChanges->RemoveElementAt(random);
214  addChangesLength--;
215 
216  /* See how big the change is, if it will fit, add it to the list of changes
217  * that we'll actually perform */
218  PRInt64 changeSize = mDevice->GetChangeSize(mDevLibrary, change);
219  if ((changesToTakeSize + changeSize) < mFreeSpace) {
220  changesToTake->AppendElement(change, PR_FALSE);
221  changesToTakeSize += changeSize;
222  }
223  }
224 
225  /* Set the changes of the changeset we are considering to the ones we selected
226  * that will fit on the device. */
227  rv = mChangeset->SetChanges(changesToTake);
228  NS_ENSURE_SUCCESS(rv, rv);
229 
230  return NS_OK;
231 }
232 
233 nsresult
235  // First find out how much room we have to use
236  nsresult rv = GetFreeSpace();
237  NS_ENSURE_SUCCESS(rv, rv);
238 
239  // Then find out which of the changes we are considering will fit
240  rv = RemoveExtraItems();
241  NS_ENSURE_SUCCESS(rv, rv);
242 
243  return NS_OK;
244 }
return NS_OK
nsresult GetMusicFreeSpace(sbILibrary *aLibrary, PRInt64 *aFreeMusicSpace)
const NS_ERROR_ABORT
Songbird Variant Utility Definitions.
static nsresult QueryUserSpaceExceeded(sbIDevice *aDevice, sbIDeviceLibrary *aLibrary, PRInt64 aSpaceNeeded, PRInt64 aSpaceAvailable, PRBool *aAbort)
var t
void SetEnsureSpaceChecked(bool aChecked)
Definition: sbBaseDevice.h:601
PRInt64 nsString_ToInt64(const nsAString &str, nsresult *rv)
bool GetEnsureSpaceChecked() const
Definition: sbBaseDevice.h:594
sbDeviceEnsureSpaceForWrite(sbBaseDevice *aDevice, sbIDeviceLibrary *aDevLibrary, sbILibraryChangeset *aChangeset)
_getSelectedPageStyle s i
const unsigned long ADDED
PRInt64 GetChangeSize(sbIDeviceLibrary *aDestLibrary, sbILibraryChange *aChange)
#define SB_DEVICE_PROPERTY_FREE_SPACE